

<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=auto>



<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/myblog/img/fluid.png">
  <link rel="icon" href="/myblog/img/fluid.png">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="author" content="ZTY">
  <meta name="keywords" content="">
  
    <meta name="description" content="SpringSecurity学习笔记(B站三更草堂)。">
<meta property="og:type" content="article">
<meta property="og:title" content="SpringSecurity学习笔记">
<meta property="og:url" content="https://zty-f.gitee.io/myblog/2022/12/01/SpringSecurity%E5%AD%A6%E4%B9%A0/index.html">
<meta property="og:site_name" content="zty-f">
<meta property="og:description" content="SpringSecurity学习笔记(B站三更草堂)。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://s3.bmp.ovh/imgs/2022/11/29/a554366a1ee9db03.png">
<meta property="article:published_time" content="2022-12-01T06:00:14.000Z">
<meta property="article:modified_time" content="2024-12-28T02:09:42.874Z">
<meta property="article:author" content="ZTY">
<meta property="article:tag" content="Java">
<meta property="article:tag" content="基础">
<meta property="article:tag" content="SpringSecurity">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://s3.bmp.ovh/imgs/2022/11/29/a554366a1ee9db03.png">
  
  
    <meta name="referrer" content="no-referrer-when-downgrade">
  
  
  <title>SpringSecurity学习笔记 - zty-f</title>

  <link  rel="stylesheet" href="https://lib.baomitu.com/twitter-bootstrap/4.6.1/css/bootstrap.min.css" />



  <link  rel="stylesheet" href="https://lib.baomitu.com/github-markdown-css/4.0.0/github-markdown.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/hint.css/2.7.0/hint.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.css" />



<!-- 主题依赖的图标库，不要自行修改 -->
<!-- Do not modify the link that theme dependent icons -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_hj8rtnfg7um.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_lbnruvf0jn.css">


<link  rel="stylesheet" href="/myblog/css/main.css" />


  <link id="highlight-css" rel="stylesheet" href="/myblog/css/highlight.css" />
  
    <link id="highlight-css-dark" rel="stylesheet" href="/myblog/css/highlight-dark.css" />
  



  
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_3799348_11m20qbqwhmo.css">



  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    Fluid.ctx = Object.assign({}, Fluid.ctx)
    var CONFIG = {"hostname":"zty-f.gitee.io","root":"/myblog/","version":"1.9.3","typing":{"enable":true,"typeSpeed":150,"cursorChar":"_","loop":true,"scope":[]},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"left","visible":"hover","icon":""},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"code_language":{"enable":true,"default":"TEXT"},"copy_btn":true,"image_caption":{"enable":true},"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"placement":"right","headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":0},"lazyload":{"enable":true,"loading_img":"/img/loading.gif","onlypost":false,"offset_factor":2},"web_analytics":{"enable":false,"follow_dnt":true,"baidu":null,"google":null,"gtag":null,"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"app_id":null,"app_key":null,"server_url":null,"path":"window.location.pathname","ignore_local":false}},"search_path":"/myblog/local-search.xml"};

    if (CONFIG.web_analytics.follow_dnt) {
      var dntVal = navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
      Fluid.ctx.dnt = dntVal && (dntVal.startsWith('1') || dntVal.startsWith('yes') || dntVal.startsWith('on'));
    }
  </script>
  <script  src="/myblog/js/utils.js" ></script>
  <script  src="/myblog/js/color-schema.js" ></script>
  


  
<meta name="generator" content="Hexo 6.3.0"></head>


<body>
  

  <header>
    

<div class="header-inner" style="height: 70vh;">
  <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand" href="/myblog/">
      <strong>思前想后</strong>
    </a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/myblog/">
                <i class="iconfont icon-home-fill"></i>
                首页
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item dropdown">
              <a class="nav-link dropdown-toggle" target="_self" href="javascript:;" role="button"
                 data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                <i class="iconfont icon-books"></i>
                魔盒
              </a>
              <div class="dropdown-menu" aria-labelledby="navbarDropdown">
                
                  
                  
                  
                  <a class="dropdown-item" href="/myblog/archives/">
                    <i class="iconfont icon-archive-fill"></i>
                    归档
                  </a>
                
                  
                  
                  
                  <a class="dropdown-item" href="/myblog/categories/">
                    <i class="iconfont icon-yingyong"></i>
                    分类
                  </a>
                
                  
                  
                  
                  <a class="dropdown-item" href="/myblog/tags/">
                    <i class="iconfont icon-tags-fill"></i>
                    标签
                  </a>
                
              </div>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/myblog/about/">
                <i class="iconfont icon-user-fill"></i>
                关于
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/myblog/links/">
                <i class="iconfont icon-link-fill"></i>
                友链
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" target="_blank" rel="noopener" href="https://github.com/zty-f">
                <i class="iconfont icon-GitHub"></i>
                
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalSearch" aria-label="Search">
              &nbsp;<i class="iconfont icon-search"></i>&nbsp;
            </a>
          </li>
          
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self" href="javascript:;" aria-label="Color Toggle">&nbsp;<i
                class="iconfont icon-dark" id="color-toggle-icon"></i>&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

  

<div id="banner" class="banner" parallax=true
     style="background: url('/myblog/img/default.png') no-repeat center center; background-size: cover;">
  <div class="full-bg-img">
    <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
      <div class="banner-text text-center fade-in-up">
        <div class="h2">
          
            <span id="subtitle" data-typed-text="SpringSecurity学习笔记"></span>
          
        </div>

        
          
  <div class="mt-3">
    
      <span class="post-meta mr-2">
        <i class="iconfont icon-author" aria-hidden="true"></i>
        ZTY
      </span>
    
    
      <span class="post-meta">
        <i class="iconfont icon-date-fill" aria-hidden="true"></i>
        <time datetime="2022-12-01 14:00" pubdate>
          2022年12月1日 下午
        </time>
      </span>
    
  </div>

  <div class="mt-1">
    
      <span class="post-meta mr-2">
        <i class="iconfont icon-chart"></i>
        
          46k 字
        
      </span>
    

    
      <span class="post-meta mr-2">
        <i class="iconfont icon-clock-fill"></i>
        
        
        
          381 分钟
        
      </span>
    

    
    
      
        <span id="busuanzi_container_page_pv" style="display: none">
          <i class="iconfont icon-eye" aria-hidden="true"></i>
          <span id="busuanzi_value_page_pv"></span> 次
        </span>
        
      
    
  </div>


        
      </div>

      
    </div>
  </div>
</div>

</div>

  </header>

  <main>
    
      

<div class="container-fluid nopadding-x">
  <div class="row nomargin-x">
    <div class="side-col d-none d-lg-block col-lg-2">
      

    </div>

    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div id="board">
          <article class="post-content mx-auto">
            <!-- SEO header -->
            <h1 style="display: none">SpringSecurity学习笔记</h1>
            
            
              <div class="markdown-body">
                
                <h1 id="SpringSecurity学习"><a href="#SpringSecurity学习" class="headerlink" title="SpringSecurity学习"></a>SpringSecurity学习</h1><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p> <strong>Spring Security</strong> 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架 <strong>Shiro</strong>，它提供了更丰富的功能，社区资源也比 Shiro 丰富。</p>
<p> 一般来说中大型的项目都是使用 <strong>SpringSecurity</strong> 来做安全框架。小项目有 Shiro 的比较多，因为相比与 SpringSecurity，Shiro 的上手更加的简单。</p>
<p> 一般 Web 应用的需要进行<strong>认证</strong>和<strong>授权</strong>。</p>
<p> <strong>认证：验证当前访问系统的是不是本系统的用户，并且要确认具体是哪个用户</strong></p>
<p> <strong>授权：经过认证后判断当前用户是否有权限进行某个操作</strong></p>
<p> 而认证和授权也是 SpringSecurity 作为安全框架的核心功能。</p>
<h2 id="1-快速入门"><a href="#1-快速入门" class="headerlink" title="1. 快速入门"></a>1. 快速入门</h2><h3 id="1-1-准备工作"><a href="#1-1-准备工作" class="headerlink" title="1.1 准备工作"></a>1.1 准备工作</h3><p> 我们先要搭建一个简单的 SpringBoot 工程</p>
<p>① 设置父工程 添加依赖</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs java">&lt;parent&gt;<br>    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;<br>    &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;<br>    &lt;version&gt;<span class="hljs-number">2.5</span><span class="hljs-number">.0</span>&lt;/version&gt;<br>&lt;/parent&gt;<br>&lt;dependencies&gt;<br>    &lt;dependency&gt;<br>        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;<br>        &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;<br>    &lt;/dependency&gt;<br>    &lt;dependency&gt;<br>        &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;<br>        &lt;artifactId&gt;lombok&lt;/artifactId&gt;<br>        &lt;optional&gt;<span class="hljs-literal">true</span>&lt;/optional&gt;<br>    &lt;/dependency&gt;<br>&lt;/dependencies&gt;<br></code></pre></td></tr></table></figure>



<p>② 创建启动类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@SpringBootApplication</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SecurityApplication</span> &#123;<br> <br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>        SpringApplication.run(SecurityApplication.class,args);<br>    &#125;<br>&#125;<br> <br></code></pre></td></tr></table></figure>



<p>③ 创建 Controller</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RequestMapping;<br><span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;<br> <br><span class="hljs-meta">@RestController</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">HelloController</span> &#123;<br> <br>    <span class="hljs-meta">@RequestMapping(&quot;/hello&quot;)</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">hello</span><span class="hljs-params">()</span>&#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;hello&quot;</span>;<br>    &#125;<br>&#125;<br> <br></code></pre></td></tr></table></figure>



<h3 id="1-2-引入-SpringSecurity"><a href="#1-2-引入-SpringSecurity" class="headerlink" title="1.2 引入 SpringSecurity"></a>1.2 引入 SpringSecurity</h3><p> 在 SpringBoot 项目中使用 SpringSecurity 我们只需要引入依赖即可实现入门案例。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java">&lt;dependency&gt;<br>    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;<br>    &lt;artifactId&gt;spring-boot-starter-security&lt;/artifactId&gt;<br>&lt;/dependency&gt;<br></code></pre></td></tr></table></figure>



<p> 引入依赖后我们在尝试去访问之前的接口就会自动跳转到一个 SpringSecurity 的默认登陆页面，默认用户名是 user, 密码会输出在控制台。</p>
<p> 必须登陆之后才能对接口进行访问。</p>
<h2 id="2-认证"><a href="#2-认证" class="headerlink" title="2. 认证"></a>2. 认证</h2><h3 id="2-1-登陆校验流程"><a href="#2-1-登陆校验流程" class="headerlink" title="2.1 登陆校验流程"></a>2.1 登陆校验流程</h3><p><img src="https://s3.bmp.ovh/imgs/2022/12/01/0009c8d8f3b411d5.png" srcset="/myblog/img/loading.gif" lazyload></p>
<h3 id="2-2-原理初探"><a href="#2-2-原理初探" class="headerlink" title="2.2 原理初探"></a>2.2 原理初探</h3><p> 想要知道如何实现自己的登陆流程就必须要先知道入门案例中 SpringSecurity 的流程。</p>
<p>2.2.1 SpringSecurity 完整流程</p>
<p> SpringSecurity 的原理其实就是一个过滤器链，内部包含了提供各种功能的过滤器。这里我们可以看看入门案例中的过滤器。</p>
<p><img src="https://s3.bmp.ovh/imgs/2022/12/01/36708ab26d2ce7bf.png" srcset="/myblog/img/loading.gif" lazyload></p>
<p> 图中只展示了核心过滤器，其它的非核心过滤器并没有在图中展示。</p>
<p><strong>UsernamePasswordAuthenticationFilter</strong>: 负责处理我们在登陆页面填写了用户名密码后的登陆请求。入门案例的认证工作主要有它负责。</p>
<p><strong>ExceptionTranslationFilter：</strong> 处理过滤器链中抛出的任何 AccessDeniedException 和 AuthenticationException 。</p>
<p><strong>FilterSecurityInterceptor：</strong> 负责权限校验的过滤器。</p>
<p> 我们可以通过 Debug 查看当前系统中 SpringSecurity 过滤器链中有哪些过滤器及它们的顺序。</p>
<p><img src="https://s3.bmp.ovh/imgs/2022/12/01/bbd05a0b451ed608.png" srcset="/myblog/img/loading.gif" lazyload></p>
<p>2.2.2 认证流程详解</p>
<p><img src="https://s3.bmp.ovh/imgs/2022/12/01/e29327ad2b30cab6.png" srcset="/myblog/img/loading.gif" lazyload></p>
<p>概念速查:</p>
<p>Authentication 接口: 它的实现类，表示当前访问系统的用户，封装了用户相关信息。</p>
<p>AuthenticationManager 接口：定义了认证 Authentication 的方法</p>
<p>UserDetailsService 接口：加载用户特定数据的核心接口。里面定义了一个根据用户名查询用户信息的方法。</p>
<p>UserDetails 接口：提供核心用户信息。通过 UserDetailsService 根据用户名获取处理的用户信息要封装成 UserDetails 对象返回。然后将这些信息封装到 Authentication 对象中。</p>
<h3 id="2-3-解决问题"><a href="#2-3-解决问题" class="headerlink" title="2.3 解决问题"></a>2.3 解决问题</h3><p>2.3.1 思路分析</p>
<p>登录</p>
<p> ①自定义登录接口</p>
<p> 调用 ProviderManager 的方法进行认证 如果认证通过生成 jwt</p>
<p> 把用户信息存入 redis 中</p>
<p> ②自定义 UserDetailsService</p>
<p> 在这个实现类中去查询数据库</p>
<p>校验：</p>
<p> ①定义 Jwt 认证过滤器</p>
<p> 获取 token</p>
<p> 解析 token 获取其中的 userid</p>
<p> 从 redis 中获取用户信息</p>
<p> 存入 SecurityContextHolder</p>
<p>2.3.2 准备工作</p>
<p>①添加依赖</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs java">&lt;!--redis依赖--&gt;<br>&lt;dependency&gt;<br>    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;<br>    &lt;artifactId&gt;spring-boot-starter-data-redis&lt;/artifactId&gt;<br>&lt;/dependency&gt;<br>&lt;!--fastjson依赖--&gt;<br>&lt;dependency&gt;<br>    &lt;groupId&gt;com.alibaba&lt;/groupId&gt;<br>    &lt;artifactId&gt;fastjson&lt;/artifactId&gt;<br>    &lt;version&gt;<span class="hljs-number">1.2</span><span class="hljs-number">.33</span>&lt;/version&gt;<br>&lt;/dependency&gt;<br>&lt;!--jwt依赖--&gt;<br>&lt;dependency&gt;<br>    &lt;groupId&gt;io.jsonwebtoken&lt;/groupId&gt;<br>    &lt;artifactId&gt;jjwt&lt;/artifactId&gt;<br>    &lt;version&gt;<span class="hljs-number">0.9</span><span class="hljs-number">.0</span>&lt;/version&gt;<br>&lt;/dependency&gt;<br></code></pre></td></tr></table></figure>



<p>② 添加 Redis 相关配置</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> com.alibaba.fastjson.JSON;<br><span class="hljs-keyword">import</span> com.alibaba.fastjson.serializer.SerializerFeature;<br><span class="hljs-keyword">import</span> com.fasterxml.jackson.databind.JavaType;<br><span class="hljs-keyword">import</span> com.fasterxml.jackson.databind.ObjectMapper;<br><span class="hljs-keyword">import</span> com.fasterxml.jackson.databind.type.TypeFactory;<br><span class="hljs-keyword">import</span> org.springframework.data.redis.serializer.RedisSerializer;<br><span class="hljs-keyword">import</span> org.springframework.data.redis.serializer.SerializationException;<br><span class="hljs-keyword">import</span> com.alibaba.fastjson.parser.ParserConfig;<br><span class="hljs-keyword">import</span> org.springframework.util.Assert;<br><span class="hljs-keyword">import</span> java.nio.charset.Charset;<br> <br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * Redis使用FastJson序列化</span><br><span class="hljs-comment"> * </span><br><span class="hljs-comment"> * <span class="hljs-doctag">@author</span> sg</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">FastJsonRedisSerializer</span>&lt;T&gt; <span class="hljs-keyword">implements</span> <span class="hljs-title class_">RedisSerializer</span>&lt;T&gt;<br>&#123;<br> <br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Charset</span> <span class="hljs-variable">DEFAULT_CHARSET</span> <span class="hljs-operator">=</span> Charset.forName(<span class="hljs-string">&quot;UTF-8&quot;</span>);<br> <br>    <span class="hljs-keyword">private</span> Class&lt;T&gt; clazz;<br> <br>    <span class="hljs-keyword">static</span><br>    &#123;<br>        ParserConfig.getGlobalInstance().setAutoTypeSupport(<span class="hljs-literal">true</span>);<br>    &#125;<br> <br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">FastJsonRedisSerializer</span><span class="hljs-params">(Class&lt;T&gt; clazz)</span><br>    &#123;<br>        <span class="hljs-built_in">super</span>();<br>        <span class="hljs-built_in">this</span>.clazz = clazz;<br>    &#125;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">byte</span>[] serialize(T t) <span class="hljs-keyword">throws</span> SerializationException<br>    &#123;<br>        <span class="hljs-keyword">if</span> (t == <span class="hljs-literal">null</span>)<br>        &#123;<br>            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">byte</span>[<span class="hljs-number">0</span>];<br>        &#125;<br>        <span class="hljs-keyword">return</span> JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);<br>    &#125;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> T <span class="hljs-title function_">deserialize</span><span class="hljs-params">(<span class="hljs-type">byte</span>[] bytes)</span> <span class="hljs-keyword">throws</span> SerializationException<br>    &#123;<br>        <span class="hljs-keyword">if</span> (bytes == <span class="hljs-literal">null</span> || bytes.length &lt;= <span class="hljs-number">0</span>)<br>        &#123;<br>            <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br>        &#125;<br>        <span class="hljs-type">String</span> <span class="hljs-variable">str</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>(bytes, DEFAULT_CHARSET);<br> <br>        <span class="hljs-keyword">return</span> JSON.parseObject(str, clazz);<br>    &#125;<br> <br> <br>    <span class="hljs-keyword">protected</span> JavaType <span class="hljs-title function_">getJavaType</span><span class="hljs-params">(Class&lt;?&gt; clazz)</span><br>    &#123;<br>        <span class="hljs-keyword">return</span> TypeFactory.defaultInstance().constructType(clazz);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> org.springframework.context.annotation.Bean;<br><span class="hljs-keyword">import</span> org.springframework.context.annotation.Configuration;<br><span class="hljs-keyword">import</span> org.springframework.data.redis.connection.RedisConnectionFactory;<br><span class="hljs-keyword">import</span> org.springframework.data.redis.core.RedisTemplate;<br><span class="hljs-keyword">import</span> org.springframework.data.redis.serializer.StringRedisSerializer;<br> <br><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">RedisConfig</span> &#123;<br> <br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-meta">@SuppressWarnings(value = &#123; &quot;unchecked&quot;, &quot;rawtypes&quot; &#125;)</span><br>    <span class="hljs-keyword">public</span> RedisTemplate&lt;Object, Object&gt; <span class="hljs-title function_">redisTemplate</span><span class="hljs-params">(RedisConnectionFactory connectionFactory)</span><br>    &#123;<br>        RedisTemplate&lt;Object, Object&gt; template = <span class="hljs-keyword">new</span> <span class="hljs-title class_">RedisTemplate</span>&lt;&gt;();<br>        template.setConnectionFactory(connectionFactory);<br> <br>        <span class="hljs-type">FastJsonRedisSerializer</span> <span class="hljs-variable">serializer</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FastJsonRedisSerializer</span>(Object.class);<br> <br>        <span class="hljs-comment">// 使用StringRedisSerializer来序列化和反序列化redis的key值</span><br>        template.setKeySerializer(<span class="hljs-keyword">new</span> <span class="hljs-title class_">StringRedisSerializer</span>());<br>        template.setValueSerializer(serializer);<br> <br>        <span class="hljs-comment">// Hash的key也采用StringRedisSerializer的序列化方式</span><br>        template.setHashKeySerializer(<span class="hljs-keyword">new</span> <span class="hljs-title class_">StringRedisSerializer</span>());<br>        template.setHashValueSerializer(serializer);<br> <br>        template.afterPropertiesSet();<br>        <span class="hljs-keyword">return</span> template;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>



<p>③ 响应类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> com.fasterxml.jackson.annotation.JsonInclude;<br> <br><span class="hljs-meta">@JsonInclude(JsonInclude.Include.NON_NULL)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ResponseResult</span>&lt;T&gt; &#123;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 状态码</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> Integer code;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 提示信息，如果有错误时，前端可以获取该字段进行提示</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> String msg;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 查询到的结果数据，</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">private</span> T data;<br> <br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">ResponseResult</span><span class="hljs-params">(Integer code, String msg)</span> &#123;<br>        <span class="hljs-built_in">this</span>.code = code;<br>        <span class="hljs-built_in">this</span>.msg = msg;<br>    &#125;<br> <br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">ResponseResult</span><span class="hljs-params">(Integer code, T data)</span> &#123;<br>        <span class="hljs-built_in">this</span>.code = code;<br>        <span class="hljs-built_in">this</span>.data = data;<br>    &#125;<br> <br>    <span class="hljs-keyword">public</span> Integer <span class="hljs-title function_">getCode</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> code;<br>    &#125;<br> <br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setCode</span><span class="hljs-params">(Integer code)</span> &#123;<br>        <span class="hljs-built_in">this</span>.code = code;<br>    &#125;<br> <br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">getMsg</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> msg;<br>    &#125;<br> <br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setMsg</span><span class="hljs-params">(String msg)</span> &#123;<br>        <span class="hljs-built_in">this</span>.msg = msg;<br>    &#125;<br> <br>    <span class="hljs-keyword">public</span> T <span class="hljs-title function_">getData</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> data;<br>    &#125;<br> <br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setData</span><span class="hljs-params">(T data)</span> &#123;<br>        <span class="hljs-built_in">this</span>.data = data;<br>    &#125;<br> <br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">ResponseResult</span><span class="hljs-params">(Integer code, String msg, T data)</span> &#123;<br>        <span class="hljs-built_in">this</span>.code = code;<br>        <span class="hljs-built_in">this</span>.msg = msg;<br>        <span class="hljs-built_in">this</span>.data = data;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>



<p>④工具类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> io.jsonwebtoken.Claims;<br><span class="hljs-keyword">import</span> io.jsonwebtoken.JwtBuilder;<br><span class="hljs-keyword">import</span> io.jsonwebtoken.Jwts;<br><span class="hljs-keyword">import</span> io.jsonwebtoken.SignatureAlgorithm;<br> <br><span class="hljs-keyword">import</span> javax.crypto.SecretKey;<br><span class="hljs-keyword">import</span> javax.crypto.spec.SecretKeySpec;<br><span class="hljs-keyword">import</span> java.util.Base64;<br><span class="hljs-keyword">import</span> java.util.Date;<br><span class="hljs-keyword">import</span> java.util.UUID;<br> <br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * JWT工具类</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">JwtUtil</span> &#123;<br> <br>    <span class="hljs-comment">//有效期为</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Long</span> <span class="hljs-variable">JWT_TTL</span> <span class="hljs-operator">=</span> <span class="hljs-number">60</span> * <span class="hljs-number">60</span> *<span class="hljs-number">1000L</span>;<span class="hljs-comment">// 60 * 60 *1000  一个小时</span><br>    <span class="hljs-comment">//设置秘钥明文</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">JWT_KEY</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;zty&quot;</span>;<br> <br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">getUUID</span><span class="hljs-params">()</span>&#123;<br>        <span class="hljs-type">String</span> <span class="hljs-variable">token</span> <span class="hljs-operator">=</span> UUID.randomUUID().toString().replaceAll(<span class="hljs-string">&quot;-&quot;</span>, <span class="hljs-string">&quot;&quot;</span>);<br>        <span class="hljs-keyword">return</span> token;<br>    &#125;<br>    <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 生成jtw</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> subject token中要存放的数据（json格式）</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">createJWT</span><span class="hljs-params">(String subject)</span> &#123;<br>        <span class="hljs-type">JwtBuilder</span> <span class="hljs-variable">builder</span> <span class="hljs-operator">=</span> getJwtBuilder(subject, <span class="hljs-literal">null</span>, getUUID());<span class="hljs-comment">// 设置过期时间</span><br>        <span class="hljs-keyword">return</span> builder.compact();<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 生成jtw</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> subject token中要存放的数据（json格式）</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> ttlMillis token超时时间</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">createJWT</span><span class="hljs-params">(String subject, Long ttlMillis)</span> &#123;<br>        <span class="hljs-type">JwtBuilder</span> <span class="hljs-variable">builder</span> <span class="hljs-operator">=</span> getJwtBuilder(subject, ttlMillis, getUUID());<span class="hljs-comment">// 设置过期时间</span><br>        <span class="hljs-keyword">return</span> builder.compact();<br>    &#125;<br> <br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> JwtBuilder <span class="hljs-title function_">getJwtBuilder</span><span class="hljs-params">(String subject, Long ttlMillis, String uuid)</span> &#123;<br>        <span class="hljs-type">SignatureAlgorithm</span> <span class="hljs-variable">signatureAlgorithm</span> <span class="hljs-operator">=</span> SignatureAlgorithm.HS256;<br>        <span class="hljs-type">SecretKey</span> <span class="hljs-variable">secretKey</span> <span class="hljs-operator">=</span> generalKey();<br>        <span class="hljs-type">long</span> <span class="hljs-variable">nowMillis</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br>        <span class="hljs-type">Date</span> <span class="hljs-variable">now</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(nowMillis);<br>        <span class="hljs-keyword">if</span>(ttlMillis==<span class="hljs-literal">null</span>)&#123;<br>            ttlMillis=JwtUtil.JWT_TTL;<br>        &#125;<br>        <span class="hljs-type">long</span> <span class="hljs-variable">expMillis</span> <span class="hljs-operator">=</span> nowMillis + ttlMillis;<br>        <span class="hljs-type">Date</span> <span class="hljs-variable">expDate</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(expMillis);<br>        <span class="hljs-keyword">return</span> Jwts.builder()<br>                .setId(uuid)              <span class="hljs-comment">//唯一的ID</span><br>                .setSubject(subject)   <span class="hljs-comment">// 主题  可以是JSON数据</span><br>                .setIssuer(<span class="hljs-string">&quot;sg&quot;</span>)     <span class="hljs-comment">// 签发者</span><br>                .setIssuedAt(now)      <span class="hljs-comment">// 签发时间</span><br>                .signWith(signatureAlgorithm, secretKey) <span class="hljs-comment">//使用HS256对称加密算法签名, 第二个参数为秘钥</span><br>                .setExpiration(expDate);<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 创建token</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> id</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> subject</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> ttlMillis</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">createJWT</span><span class="hljs-params">(String id, String subject, Long ttlMillis)</span> &#123;<br>        <span class="hljs-type">JwtBuilder</span> <span class="hljs-variable">builder</span> <span class="hljs-operator">=</span> getJwtBuilder(subject, ttlMillis, id);<span class="hljs-comment">// 设置过期时间</span><br>        <span class="hljs-keyword">return</span> builder.compact();<br>    &#125;<br> <br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>        <span class="hljs-type">String</span> <span class="hljs-variable">token</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJjYWM2ZDVhZi1mNjVlLTQ0MDAtYjcxMi0zYWEwOGIyOTIwYjQiLCJzdWIiOiJzZyIsImlzcyI6InNnIiwiaWF0IjoxNjM4MTA2NzEyLCJleHAiOjE2MzgxMTAzMTJ9.JVsSbkP94wuczb4QryQbAke3ysBDIL5ou8fWsbt_ebg&quot;</span>;<br>        <span class="hljs-type">Claims</span> <span class="hljs-variable">claims</span> <span class="hljs-operator">=</span> parseJWT(token);<br>        System.out.println(claims);<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 生成加密后的秘钥 secretKey</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> SecretKey <span class="hljs-title function_">generalKey</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-type">byte</span>[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);<br>        <span class="hljs-type">SecretKey</span> <span class="hljs-variable">key</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">SecretKeySpec</span>(encodedKey, <span class="hljs-number">0</span>, encodedKey.length, <span class="hljs-string">&quot;AES&quot;</span>);<br>        <span class="hljs-keyword">return</span> key;<br>    &#125;<br>    <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 解析</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> jwt</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment">     * <span class="hljs-doctag">@throws</span> Exception</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Claims <span class="hljs-title function_">parseJWT</span><span class="hljs-params">(String jwt)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>        <span class="hljs-type">SecretKey</span> <span class="hljs-variable">secretKey</span> <span class="hljs-operator">=</span> generalKey();<br>        <span class="hljs-keyword">return</span> Jwts.parser()<br>                .setSigningKey(secretKey)<br>                .parseClaimsJws(jwt)<br>                .getBody();<br>    &#125;<br> <br> <br>&#125;<br></code></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> java.util.*;<br><span class="hljs-keyword">import</span> java.util.concurrent.TimeUnit;<br> <br><span class="hljs-meta">@SuppressWarnings(value = &#123; &quot;unchecked&quot;, &quot;rawtypes&quot; &#125;)</span><br><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">RedisCache</span><br>&#123;<br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">public</span> RedisTemplate redisTemplate;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 缓存基本的对象，Integer、String、实体类等</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> key 缓存的键值</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> value 缓存的值</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> &lt;T&gt; <span class="hljs-keyword">void</span> <span class="hljs-title function_">setCacheObject</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String key, <span class="hljs-keyword">final</span> T value)</span><br>    &#123;<br>        redisTemplate.opsForValue().set(key, value);<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 缓存基本的对象，Integer、String、实体类等</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> key 缓存的键值</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> value 缓存的值</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> timeout 时间</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> timeUnit 时间颗粒度</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> &lt;T&gt; <span class="hljs-keyword">void</span> <span class="hljs-title function_">setCacheObject</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String key, <span class="hljs-keyword">final</span> T value, <span class="hljs-keyword">final</span> Integer timeout, <span class="hljs-keyword">final</span> TimeUnit timeUnit)</span><br>    &#123;<br>        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 设置有效时间</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> key Redis键</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> timeout 超时时间</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> true=设置成功；false=设置失败</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">expire</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String key, <span class="hljs-keyword">final</span> <span class="hljs-type">long</span> timeout)</span><br>    &#123;<br>        <span class="hljs-keyword">return</span> expire(key, timeout, TimeUnit.SECONDS);<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 设置有效时间</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> key Redis键</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> timeout 超时时间</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> unit 时间单位</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> true=设置成功；false=设置失败</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">expire</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String key, <span class="hljs-keyword">final</span> <span class="hljs-type">long</span> timeout, <span class="hljs-keyword">final</span> TimeUnit unit)</span><br>    &#123;<br>        <span class="hljs-keyword">return</span> redisTemplate.expire(key, timeout, unit);<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 获得缓存的基本对象。</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> key 缓存键值</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 缓存键值对应的数据</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> &lt;T&gt; T <span class="hljs-title function_">getCacheObject</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String key)</span><br>    &#123;<br>        ValueOperations&lt;String, T&gt; operation = redisTemplate.opsForValue();<br>        <span class="hljs-keyword">return</span> operation.get(key);<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 删除单个对象</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> key</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">deleteObject</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String key)</span><br>    &#123;<br>        <span class="hljs-keyword">return</span> redisTemplate.delete(key);<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 删除集合对象</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> collection 多个对象</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">long</span> <span class="hljs-title function_">deleteObject</span><span class="hljs-params">(<span class="hljs-keyword">final</span> Collection collection)</span><br>    &#123;<br>        <span class="hljs-keyword">return</span> redisTemplate.delete(collection);<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 缓存List数据</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> key 缓存的键值</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> dataList 待缓存的List数据</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 缓存的对象</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> &lt;T&gt; <span class="hljs-type">long</span> <span class="hljs-title function_">setCacheList</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String key, <span class="hljs-keyword">final</span> List&lt;T&gt; dataList)</span><br>    &#123;<br>        <span class="hljs-type">Long</span> <span class="hljs-variable">count</span> <span class="hljs-operator">=</span> redisTemplate.opsForList().rightPushAll(key, dataList);<br>        <span class="hljs-keyword">return</span> count == <span class="hljs-literal">null</span> ? <span class="hljs-number">0</span> : count;<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 获得缓存的list对象</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> key 缓存的键值</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 缓存键值对应的数据</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> &lt;T&gt; List&lt;T&gt; <span class="hljs-title function_">getCacheList</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String key)</span><br>    &#123;<br>        <span class="hljs-keyword">return</span> redisTemplate.opsForList().range(key, <span class="hljs-number">0</span>, -<span class="hljs-number">1</span>);<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 缓存Set</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> key 缓存键值</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> dataSet 缓存的数据</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 缓存数据的对象</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> &lt;T&gt; BoundSetOperations&lt;String, T&gt; <span class="hljs-title function_">setCacheSet</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String key, <span class="hljs-keyword">final</span> Set&lt;T&gt; dataSet)</span><br>    &#123;<br>        BoundSetOperations&lt;String, T&gt; setOperation = redisTemplate.boundSetOps(key);<br>        Iterator&lt;T&gt; it = dataSet.iterator();<br>        <span class="hljs-keyword">while</span> (it.hasNext())<br>        &#123;<br>            setOperation.add(it.next());<br>        &#125;<br>        <span class="hljs-keyword">return</span> setOperation;<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 获得缓存的set</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> key</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> &lt;T&gt; Set&lt;T&gt; <span class="hljs-title function_">getCacheSet</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String key)</span><br>    &#123;<br>        <span class="hljs-keyword">return</span> redisTemplate.opsForSet().members(key);<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 缓存Map</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> key</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> dataMap</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> &lt;T&gt; <span class="hljs-keyword">void</span> <span class="hljs-title function_">setCacheMap</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String key, <span class="hljs-keyword">final</span> Map&lt;String, T&gt; dataMap)</span><br>    &#123;<br>        <span class="hljs-keyword">if</span> (dataMap != <span class="hljs-literal">null</span>) &#123;<br>            redisTemplate.opsForHash().putAll(key, dataMap);<br>        &#125;<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 获得缓存的Map</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> key</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> &lt;T&gt; Map&lt;String, T&gt; <span class="hljs-title function_">getCacheMap</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String key)</span><br>    &#123;<br>        <span class="hljs-keyword">return</span> redisTemplate.opsForHash().entries(key);<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 往Hash中存入数据</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> key Redis键</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> hKey Hash键</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> value 值</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> &lt;T&gt; <span class="hljs-keyword">void</span> <span class="hljs-title function_">setCacheMapValue</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String key, <span class="hljs-keyword">final</span> String hKey, <span class="hljs-keyword">final</span> T value)</span><br>    &#123;<br>        redisTemplate.opsForHash().put(key, hKey, value);<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 获取Hash中的数据</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> key Redis键</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> hKey Hash键</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> Hash中的对象</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> &lt;T&gt; T <span class="hljs-title function_">getCacheMapValue</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String key, <span class="hljs-keyword">final</span> String hKey)</span><br>    &#123;<br>        HashOperations&lt;String, String, T&gt; opsForHash = redisTemplate.opsForHash();<br>        <span class="hljs-keyword">return</span> opsForHash.get(key, hKey);<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 删除Hash中的数据</span><br><span class="hljs-comment">     * </span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> key</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> hkey</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">delCacheMapValue</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String key, <span class="hljs-keyword">final</span> String hkey)</span><br>    &#123;<br>        <span class="hljs-type">HashOperations</span> <span class="hljs-variable">hashOperations</span> <span class="hljs-operator">=</span> redisTemplate.opsForHash();<br>        hashOperations.delete(key, hkey);<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 获取多个Hash中的数据</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> key Redis键</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> hKeys Hash键集合</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> Hash对象集合</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> &lt;T&gt; List&lt;T&gt; <span class="hljs-title function_">getMultiCacheMapValue</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String key, <span class="hljs-keyword">final</span> Collection&lt;Object&gt; hKeys)</span><br>    &#123;<br>        <span class="hljs-keyword">return</span> redisTemplate.opsForHash().multiGet(key, hKeys);<br>    &#125;<br> <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 获得缓存的基本对象列表</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> pattern 字符串前缀</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> 对象列表</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> Collection&lt;String&gt; <span class="hljs-title function_">keys</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String pattern)</span><br>    &#123;<br>        <span class="hljs-keyword">return</span> redisTemplate.keys(pattern);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> javax.servlet.http.HttpServletResponse;<br><span class="hljs-keyword">import</span> java.io.IOException;<br> <br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">WebUtils</span><br>&#123;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 将字符串渲染到客户端</span><br><span class="hljs-comment">     * </span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> response 渲染对象</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> string 待渲染的字符串</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span> null</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">renderString</span><span class="hljs-params">(HttpServletResponse response, String string)</span> &#123;<br>        <span class="hljs-keyword">try</span><br>        &#123;<br>            response.setStatus(<span class="hljs-number">200</span>);<br>            response.setContentType(<span class="hljs-string">&quot;application/json&quot;</span>);<br>            response.setCharacterEncoding(<span class="hljs-string">&quot;utf-8&quot;</span>);<br>            response.getWriter().print(string);<br>        &#125;<br>        <span class="hljs-keyword">catch</span> (IOException e)<br>        &#123;<br>            e.printStackTrace();<br>        &#125;<br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>



<p>⑤实体类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> java.io.Serializable;<br><span class="hljs-keyword">import</span> java.util.Date;<br> <br><span class="hljs-meta">@Data</span><br><span class="hljs-meta">@AllArgsConstructor</span><br><span class="hljs-meta">@NoArgsConstructor</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Serializable</span> &#123;<br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">long</span> <span class="hljs-variable">serialVersionUID</span> <span class="hljs-operator">=</span> -<span class="hljs-number">40356785423868312L</span>;<br>    <br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 主键</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> Long id;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 用户名</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> String userName;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 昵称</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> String nickName;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 密码</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> String password;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 账号状态（0正常 1停用）</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> String status;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 邮箱</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> String email;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 手机号</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> String phonenumber;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 用户性别（0男，1女，2未知）</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> String sex;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 头像</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> String avatar;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 用户类型（0管理员，1普通用户）</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> String userType;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 创建人的用户id</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> Long createBy;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 创建时间</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> Date createTime;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 更新人</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> Long updateBy;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 更新时间</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> Date updateTime;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 删除标志（0代表未删除，1代表已删除）</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> Integer delFlag;<br>&#125;<br></code></pre></td></tr></table></figure>



<h3 id="2-3-3-实现"><a href="#2-3-3-实现" class="headerlink" title="2.3.3 实现"></a>2.3.3 实现</h3><h4 id="2-3-3-1-数据库校验用户"><a href="#2-3-3-1-数据库校验用户" class="headerlink" title="2.3.3.1 数据库校验用户"></a>2.3.3.1 数据库校验用户</h4><p> 从之前的分析我们可以知道，我们可以自定义一个 UserDetailsService, 让 SpringSecurity 使用我们的 UserDetailsService。我们自己的 UserDetailsService 可以从数据库中查询用户名和密码。</p>
<p>准备工作</p>
<p> 我们先创建一个用户表， 建表语句如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs mysql">CREATE TABLE `sys_user` (<br>  `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT &#x27;主键&#x27;,<br>  `user_name` VARCHAR(64) NOT NULL DEFAULT &#x27;NULL&#x27; COMMENT &#x27;用户名&#x27;,<br>  `nick_name` VARCHAR(64) NOT NULL DEFAULT &#x27;NULL&#x27; COMMENT &#x27;昵称&#x27;,<br>  `password` VARCHAR(64) NOT NULL DEFAULT &#x27;NULL&#x27; COMMENT &#x27;密码&#x27;,<br>  `status` CHAR(1) DEFAULT &#x27;0&#x27; COMMENT &#x27;账号状态（0正常 1停用）&#x27;,<br>  `email` VARCHAR(64) DEFAULT NULL COMMENT &#x27;邮箱&#x27;,<br>  `phonenumber` VARCHAR(32) DEFAULT NULL COMMENT &#x27;手机号&#x27;,<br>  `sex` CHAR(1) DEFAULT NULL COMMENT &#x27;用户性别（0男，1女，2未知）&#x27;,<br>  `avatar` VARCHAR(128) DEFAULT NULL COMMENT &#x27;头像&#x27;,<br>  `user_type` CHAR(1) NOT NULL DEFAULT &#x27;1&#x27; COMMENT &#x27;用户类型（0管理员，1普通用户）&#x27;,<br>  `create_by` BIGINT(20) DEFAULT NULL COMMENT &#x27;创建人的用户id&#x27;,<br>  `create_time` DATETIME DEFAULT NULL COMMENT &#x27;创建时间&#x27;,<br>  `update_by` BIGINT(20) DEFAULT NULL COMMENT &#x27;更新人&#x27;,<br>  `update_time` DATETIME DEFAULT NULL COMMENT &#x27;更新时间&#x27;,<br>  `del_flag` INT(11) DEFAULT &#x27;0&#x27; COMMENT &#x27;删除标志（0代表未删除，1代表已删除）&#x27;,<br>  PRIMARY KEY (`id`)<br>) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT=&#x27;用户表&#x27;<br></code></pre></td></tr></table></figure>



<p> 引入 MybatisPuls 和 mysql 驱动的依赖</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java">&lt;dependency&gt;<br>    &lt;groupId&gt;com.baomidou&lt;/groupId&gt;<br>    &lt;artifactId&gt;mybatis-plus-boot-starter&lt;/artifactId&gt;<br>    &lt;version&gt;<span class="hljs-number">3.4</span><span class="hljs-number">.3</span>&lt;/version&gt;<br>&lt;/dependency&gt;<br>&lt;dependency&gt;<br>    &lt;groupId&gt;mysql&lt;/groupId&gt;<br>    &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;<br>&lt;/dependency&gt;<br></code></pre></td></tr></table></figure>



<p> 配置数据库信息</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java">spring:<br>  datasource:<br>    url: jdbc:mysql:<span class="hljs-comment">//localhost:3306/sg_security?characterEncoding=utf-8&amp;serverTimezone=UTC</span><br>    username: root<br>    password: root<br>    driver-class-name: com.mysql.cj.jdbc.Driver<br></code></pre></td></tr></table></figure>



<p> 定义 Mapper 接口</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">UserMapper</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">BaseMapper</span>&lt;User&gt; &#123;<br>&#125;<br></code></pre></td></tr></table></figure>



<p> 修改 User 实体类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">类名上加<span class="hljs-meta">@TableName(value = &quot;sys_user&quot;)</span> ,id字段上加 <span class="hljs-meta">@TableId</span><br></code></pre></td></tr></table></figure>



<p> 配置 Mapper 扫描</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@SpringBootApplication</span><br><span class="hljs-meta">@MapperScan(&quot;com.zty.mapper&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SimpleSecurityApplication</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>        <span class="hljs-type">ConfigurableApplicationContext</span> <span class="hljs-variable">run</span> <span class="hljs-operator">=</span> SpringApplication.run(SimpleSecurityApplication.class);<br>        System.out.println(run);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>



<p> 添加 junit 依赖</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java">&lt;dependency&gt;<br>    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;<br>    &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;<br>&lt;/dependency&gt;<br></code></pre></td></tr></table></figure>



<p> 测试 MP 是否能正常使用</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@SpringBootTest</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MapperTest</span> &#123;<br> <br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> UserMapper userMapper;<br> <br>    <span class="hljs-meta">@Test</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testUserMapper</span><span class="hljs-params">()</span>&#123;<br>        List&lt;User&gt; users = userMapper.selectList(<span class="hljs-literal">null</span>);<br>        System.out.println(users);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>



<p>核心代码实现</p>
<p>创建一个类实现 UserDetailsService 接口，重写其中的方法。更加用户名从数据库中查询用户信息</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Service</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserDetailsServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">UserDetailsService</span> &#123;<br> <br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> UserMapper userMapper;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> UserDetails <span class="hljs-title function_">loadUserByUsername</span><span class="hljs-params">(String username)</span> <span class="hljs-keyword">throws</span> UsernameNotFoundException &#123;<br>        <span class="hljs-comment">//根据用户名查询用户信息</span><br>        LambdaQueryWrapper&lt;User&gt; wrapper = <span class="hljs-keyword">new</span> <span class="hljs-title class_">LambdaQueryWrapper</span>&lt;&gt;();<br>        wrapper.eq(User::getUserName,username);<br>        <span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> userMapper.selectOne(wrapper);<br>        <span class="hljs-comment">//如果查询不到数据就通过抛出异常来给出提示</span><br>        <span class="hljs-keyword">if</span>(Objects.isNull(user))&#123;<br>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(<span class="hljs-string">&quot;用户名或密码错误&quot;</span>);<br>        &#125;<br>        <span class="hljs-comment">//TODO 根据用户查询权限信息 添加到LoginUser中</span><br>        <br>        <span class="hljs-comment">//封装成UserDetails对象返回 </span><br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">LoginUser</span>(user);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>



<p>因为 UserDetailsService 方法的返回值是 UserDetails 类型，所以需要定义一个类，实现该接口，把用户信息封装在其中。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Data</span><br><span class="hljs-meta">@NoArgsConstructor</span><br><span class="hljs-meta">@AllArgsConstructor</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LoginUser</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">UserDetails</span> &#123;<br> <br>    <span class="hljs-keyword">private</span> User user;<br> <br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> Collection&lt;? <span class="hljs-keyword">extends</span> <span class="hljs-title class_">GrantedAuthority</span>&gt; getAuthorities() &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br>    &#125;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">getPassword</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> user.getPassword();<br>    &#125;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">getUsername</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> user.getUserName();<br>    &#125;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">isAccountNonExpired</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>    &#125;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">isAccountNonLocked</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>    &#125;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">isCredentialsNonExpired</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>    &#125;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">isEnabled</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>



<p>注意：如果要测试，需要往用户表中写入用户数据，并且如果你想让用户的密码是明文存储，需要在密码前加 {noop}。例如</p>
<p><img src="https://img-blog.csdnimg.cn/a4557f33e1c54795aa0a6ca6fb302ce9.png" srcset="/myblog/img/loading.gif" lazyload alt="img"></p>
<p>这样登陆的时候就可以用 sg 作为用户名，1234 作为密码来登陆了。</p>
<p>2.3.3.2 密码加密存储</p>
<p> 实际项目中我们不会把密码明文存储在数据库中。</p>
<p> 默认使用的 PasswordEncoder 要求数据库中的密码格式为：{id}password 。它会根据 id 去判断密码的加密方式。但是我们一般不会采用这种方式。所以就需要替换 PasswordEncoder。</p>
<p> 我们一般使用 SpringSecurity 为我们提供的 BCryptPasswordEncoder。</p>
<p> 我们只需要使用把 BCryptPasswordEncoder 对象注入 Spring 容器中，SpringSecurity 就会使用该 PasswordEncoder 来进行密码校验。</p>
<p> 我们可以定义一个 SpringSecurity 的配置类，SpringSecurity 要求这个配置类要继承 WebSecurityConfigurerAdapter。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SecurityConfig</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">WebSecurityConfigurerAdapter</span> &#123;<br> <br> <br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-keyword">public</span> PasswordEncoder <span class="hljs-title function_">passwordEncoder</span><span class="hljs-params">()</span>&#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BCryptPasswordEncoder</span>();<br>    &#125;<br> <br>&#125;<br></code></pre></td></tr></table></figure>



<h4 id="2-3-3-3-登陆接口"><a href="#2-3-3-3-登陆接口" class="headerlink" title="2.3.3.3 登陆接口"></a>2.3.3.3 登陆接口</h4><p> 接下我们需要自定义登陆接口，然后让 SpringSecurity 对这个接口放行, 让用户访问这个接口的时候不用登录也能访问。</p>
<p> 在接口中我们通过 AuthenticationManager 的 authenticate 方法来进行用户认证, 所以需要在 SecurityConfig 中配置把 AuthenticationManager 注入容器。</p>
<p> 认证成功的话要生成一个 jwt，放入响应中返回。并且为了让用户下回请求时能通过 jwt 识别出具体的是哪个用户，我们需要把用户信息存入 redis，可以把用户 id 作为 key。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RestController</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LoginController</span> &#123;<br> <br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> LoginServcie loginServcie;<br> <br>    <span class="hljs-meta">@PostMapping(&quot;/user/login&quot;)</span><br>    <span class="hljs-keyword">public</span> ResponseResult <span class="hljs-title function_">login</span><span class="hljs-params">(<span class="hljs-meta">@RequestBody</span> User user)</span>&#123;<br>        <span class="hljs-keyword">return</span> loginServcie.login(user);<br>    &#125;<br>&#125;<br> <br></code></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SecurityConfig</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">WebSecurityConfigurerAdapter</span> &#123;<br> <br> <br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-keyword">public</span> PasswordEncoder <span class="hljs-title function_">passwordEncoder</span><span class="hljs-params">()</span>&#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BCryptPasswordEncoder</span>();<br>    &#125;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">configure</span><span class="hljs-params">(HttpSecurity http)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>        http<br>                <span class="hljs-comment">//关闭csrf</span><br>                .csrf().disable()<br>                <span class="hljs-comment">//不通过Session获取SecurityContext</span><br>                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)<br>                .and()<br>                .authorizeRequests()<br>                <span class="hljs-comment">// 对于登录接口 允许匿名访问</span><br>                .antMatchers(<span class="hljs-string">&quot;/user/login&quot;</span>).anonymous()<br>                <span class="hljs-comment">// 除上面外的所有请求全部需要鉴权认证</span><br>                .anyRequest().authenticated();<br>    &#125;<br> <br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> AuthenticationManager <span class="hljs-title function_">authenticationManagerBean</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-built_in">super</span>.authenticationManagerBean();<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>







<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Service</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LoginServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">LoginServcie</span> &#123;<br> <br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> AuthenticationManager authenticationManager;<br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> RedisCache redisCache;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> ResponseResult <span class="hljs-title function_">login</span><span class="hljs-params">(User user)</span> &#123;<br>        <span class="hljs-type">UsernamePasswordAuthenticationToken</span> <span class="hljs-variable">authenticationToken</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">UsernamePasswordAuthenticationToken</span>(user.getUserName(),user.getPassword());<br>        <span class="hljs-type">Authentication</span> <span class="hljs-variable">authenticate</span> <span class="hljs-operator">=</span> authenticationManager.authenticate(authenticationToken);<br>        <span class="hljs-keyword">if</span>(Objects.isNull(authenticate))&#123;<br>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(<span class="hljs-string">&quot;用户名或密码错误&quot;</span>);<br>        &#125;<br>        <span class="hljs-comment">//使用userid生成token</span><br>        <span class="hljs-type">LoginUser</span> <span class="hljs-variable">loginUser</span> <span class="hljs-operator">=</span> (LoginUser) authenticate.getPrincipal();<br>        <span class="hljs-type">String</span> <span class="hljs-variable">userId</span> <span class="hljs-operator">=</span> loginUser.getUser().getId().toString();<br>        <span class="hljs-type">String</span> <span class="hljs-variable">jwt</span> <span class="hljs-operator">=</span> JwtUtil.createJWT(userId);<br>        <span class="hljs-comment">//authenticate存入redis</span><br>        redisCache.setCacheObject(<span class="hljs-string">&quot;login:&quot;</span>+userId,loginUser);<br>        <span class="hljs-comment">//把token响应给前端</span><br>        HashMap&lt;String,String&gt; map = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span>&lt;&gt;();<br>        map.put(<span class="hljs-string">&quot;token&quot;</span>,jwt);<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ResponseResult</span>(<span class="hljs-number">200</span>,<span class="hljs-string">&quot;登陆成功&quot;</span>,map);<br>    &#125;<br>&#125;<br> <br></code></pre></td></tr></table></figure>



<p>2.3.3.4 认证过滤器</p>
<p> 我们需要自定义一个过滤器，这个过滤器会去获取请求头中的 token，对 token 进行解析取出其中的 userid。</p>
<p> 使用 userid 去 redis 中获取对应的 LoginUser 对象。</p>
<p> 然后封装 Authentication 对象存入 SecurityContextHolder</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">JwtAuthenticationTokenFilter</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">OncePerRequestFilter</span> &#123;<br> <br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> RedisCache redisCache;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">doFilterInternal</span><span class="hljs-params">(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)</span> <span class="hljs-keyword">throws</span> ServletException, IOException &#123;<br>        <span class="hljs-comment">//获取token</span><br>        <span class="hljs-type">String</span> <span class="hljs-variable">token</span> <span class="hljs-operator">=</span> request.getHeader(<span class="hljs-string">&quot;token&quot;</span>);<br>        <span class="hljs-keyword">if</span> (!StringUtils.hasText(token)) &#123;<br>            <span class="hljs-comment">//放行</span><br>            filterChain.doFilter(request, response);<br>            <span class="hljs-keyword">return</span>;<br>        &#125;<br>        <span class="hljs-comment">//解析token</span><br>        String userid;<br>        <span class="hljs-keyword">try</span> &#123;<br>            <span class="hljs-type">Claims</span> <span class="hljs-variable">claims</span> <span class="hljs-operator">=</span> JwtUtil.parseJWT(token);<br>            userid = claims.getSubject();<br>        &#125; <span class="hljs-keyword">catch</span> (Exception e) &#123;<br>            e.printStackTrace();<br>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(<span class="hljs-string">&quot;token非法&quot;</span>);<br>        &#125;<br>        <span class="hljs-comment">//从redis中获取用户信息</span><br>        <span class="hljs-type">String</span> <span class="hljs-variable">redisKey</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;login:&quot;</span> + userid;<br>        <span class="hljs-type">LoginUser</span> <span class="hljs-variable">loginUser</span> <span class="hljs-operator">=</span> redisCache.getCacheObject(redisKey);<br>        <span class="hljs-keyword">if</span>(Objects.isNull(loginUser))&#123;<br>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(<span class="hljs-string">&quot;用户未登录&quot;</span>);<br>        &#125;<br>        <span class="hljs-comment">//存入SecurityContextHolder</span><br>        <span class="hljs-comment">//TODO 获取权限信息封装到Authentication中</span><br>        <span class="hljs-type">UsernamePasswordAuthenticationToken</span> <span class="hljs-variable">authenticationToken</span> <span class="hljs-operator">=</span><br>                <span class="hljs-keyword">new</span> <span class="hljs-title class_">UsernamePasswordAuthenticationToken</span>(loginUser,<span class="hljs-literal">null</span>,<span class="hljs-literal">null</span>);<br>        SecurityContextHolder.getContext().setAuthentication(authenticationToken);<br>        <span class="hljs-comment">//放行</span><br>        filterChain.doFilter(request, response);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SecurityConfig</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">WebSecurityConfigurerAdapter</span> &#123;<br> <br> <br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-keyword">public</span> PasswordEncoder <span class="hljs-title function_">passwordEncoder</span><span class="hljs-params">()</span>&#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BCryptPasswordEncoder</span>();<br>    &#125;<br> <br> <br>    <span class="hljs-meta">@Autowired</span><br>    JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">configure</span><span class="hljs-params">(HttpSecurity http)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>        http<br>                <span class="hljs-comment">//关闭csrf</span><br>                .csrf().disable()<br>                <span class="hljs-comment">//不通过Session获取SecurityContext</span><br>                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)<br>                .and()<br>                .authorizeRequests()<br>                <span class="hljs-comment">// 对于登录接口 允许匿名访问</span><br>                .antMatchers(<span class="hljs-string">&quot;/user/login&quot;</span>).anonymous()<br>                <span class="hljs-comment">// 除上面外的所有请求全部需要鉴权认证</span><br>                .anyRequest().authenticated();<br> <br>        <span class="hljs-comment">//把token校验过滤器添加到过滤器链中</span><br>        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);<br>    &#125;<br> <br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> AuthenticationManager <span class="hljs-title function_">authenticationManagerBean</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-built_in">super</span>.authenticationManagerBean();<br>    &#125;<br>&#125;<br> <br></code></pre></td></tr></table></figure>



<p>2.3.3.5 退出登陆</p>
<p> 我们只需要定义一个登陆接口，然后获取 SecurityContextHolder 中的认证信息，删除 redis 中对应的数据即可。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Service</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LoginServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">LoginServcie</span> &#123;<br> <br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> AuthenticationManager authenticationManager;<br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> RedisCache redisCache;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> ResponseResult <span class="hljs-title function_">login</span><span class="hljs-params">(User user)</span> &#123;<br>        <span class="hljs-type">UsernamePasswordAuthenticationToken</span> <span class="hljs-variable">authenticationToken</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">UsernamePasswordAuthenticationToken</span>(user.getUserName(),user.getPassword());<br>        <span class="hljs-type">Authentication</span> <span class="hljs-variable">authenticate</span> <span class="hljs-operator">=</span> authenticationManager.authenticate(authenticationToken);<br>        <span class="hljs-keyword">if</span>(Objects.isNull(authenticate))&#123;<br>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(<span class="hljs-string">&quot;用户名或密码错误&quot;</span>);<br>        &#125;<br>        <span class="hljs-comment">//使用userid生成token</span><br>        <span class="hljs-type">LoginUser</span> <span class="hljs-variable">loginUser</span> <span class="hljs-operator">=</span> (LoginUser) authenticate.getPrincipal();<br>        <span class="hljs-type">String</span> <span class="hljs-variable">userId</span> <span class="hljs-operator">=</span> loginUser.getUser().getId().toString();<br>        <span class="hljs-type">String</span> <span class="hljs-variable">jwt</span> <span class="hljs-operator">=</span> JwtUtil.createJWT(userId);<br>        <span class="hljs-comment">//authenticate存入redis</span><br>        redisCache.setCacheObject(<span class="hljs-string">&quot;login:&quot;</span>+userId,loginUser);<br>        <span class="hljs-comment">//把token响应给前端</span><br>        HashMap&lt;String,String&gt; map = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span>&lt;&gt;();<br>        map.put(<span class="hljs-string">&quot;token&quot;</span>,jwt);<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ResponseResult</span>(<span class="hljs-number">200</span>,<span class="hljs-string">&quot;登陆成功&quot;</span>,map);<br>    &#125;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> ResponseResult <span class="hljs-title function_">logout</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-type">Authentication</span> <span class="hljs-variable">authentication</span> <span class="hljs-operator">=</span> SecurityContextHolder.getContext().getAuthentication();<br>        <span class="hljs-type">LoginUser</span> <span class="hljs-variable">loginUser</span> <span class="hljs-operator">=</span> (LoginUser) authentication.getPrincipal();<br>        <span class="hljs-type">Long</span> <span class="hljs-variable">userid</span> <span class="hljs-operator">=</span> loginUser.getUser().getId();<br>        redisCache.deleteObject(<span class="hljs-string">&quot;login:&quot;</span>+userid);<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ResponseResult</span>(<span class="hljs-number">200</span>,<span class="hljs-string">&quot;退出成功&quot;</span>);<br>    &#125;<br>&#125;<br> <br></code></pre></td></tr></table></figure>



<h2 id="3-授权"><a href="#3-授权" class="headerlink" title="3. 授权"></a>3. 授权</h2><h3 id="3-0-权限系统的作用"><a href="#3-0-权限系统的作用" class="headerlink" title="3.0 权限系统的作用"></a>3.0 权限系统的作用</h3><p> 例如一个学校图书馆的管理系统，如果是普通学生登录就能看到借书还书相关的功能，不可能让他看到并且去使用添加书籍信息，删除书籍信息等功能。但是如果是一个图书馆管理员的账号登录了，应该就能看到并使用添加书籍信息，删除书籍信息等功能。</p>
<p> 总结起来就是<strong>不同的用户可以使用不同的功能</strong>。这就是权限系统要去实现的效果。</p>
<p> 我们不能只依赖前端去判断用户的权限来选择显示哪些菜单哪些按钮。因为如果只是这样，如果有人知道了对应功能的接口地址就可以不通过前端，直接去发送请求来实现相关功能操作。</p>
<p> 所以我们还需要在后台进行用户权限的判断，判断当前用户是否有相应的权限，必须具有所需权限才能进行相应的操作。</p>
<h3 id="3-1-授权基本流程"><a href="#3-1-授权基本流程" class="headerlink" title="3.1 授权基本流程"></a>3.1 授权基本流程</h3><p> 在 SpringSecurity 中，会使用默认的 FilterSecurityInterceptor 来进行权限校验。在 FilterSecurityInterceptor 中会从 SecurityContextHolder 获取其中的 Authentication，然后获取其中的权限信息。当前用户是否拥有访问当前资源所需的权限。</p>
<p> 所以我们在项目中只需要把当前登录用户的权限信息也存入 Authentication。</p>
<p> 然后设置我们的资源所需要的权限即可。</p>
<h3 id="3-2-授权实现"><a href="#3-2-授权实现" class="headerlink" title="3.2 授权实现"></a>3.2 授权实现</h3><p>3.2.1 限制访问资源所需权限</p>
<p> SpringSecurity 为我们提供了基于注解的权限控制方案，这也是我们项目中主要采用的方式。我们可以使用注解去指定访问对应的资源所需的权限。</p>
<p> 但是要使用它我们需要先开启相关配置。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@EnableGlobalMethodSecurity(prePostEnabled = true)</span><br></code></pre></td></tr></table></figure>



<p> 然后就可以使用对应的注解。@PreAuthorize</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RestController</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">HelloController</span> &#123;<br> <br>    <span class="hljs-meta">@RequestMapping(&quot;/hello&quot;)</span><br>    <span class="hljs-meta">@PreAuthorize(&quot;hasAuthority(&#x27;test&#x27;)&quot;)</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">hello</span><span class="hljs-params">()</span>&#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;hello&quot;</span>;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>



<p>3.2.2 封装权限信息</p>
<p> 我们前面在写 UserDetailsServiceImpl 的时候说过，在查询出用户后还要获取对应的权限信息，封装到 UserDetails 中返回。</p>
<p> 我们先直接把权限信息写死封装到 UserDetails 中进行测试。</p>
<p> 我们之前定义了 UserDetails 的实现类 LoginUser，想要让其能封装权限信息就要对其进行修改。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.zty.domain;<br> <br><span class="hljs-keyword">import</span> com.alibaba.fastjson.annotation.JSONField;<br><span class="hljs-keyword">import</span> lombok.AllArgsConstructor;<br><span class="hljs-keyword">import</span> lombok.Data;<br><span class="hljs-keyword">import</span> lombok.NoArgsConstructor;<br><span class="hljs-keyword">import</span> org.springframework.security.core.GrantedAuthority;<br><span class="hljs-keyword">import</span> org.springframework.security.core.authority.SimpleGrantedAuthority;<br><span class="hljs-keyword">import</span> org.springframework.security.core.userdetails.UserDetails;<br> <br><span class="hljs-keyword">import</span> java.util.Collection;<br><span class="hljs-keyword">import</span> java.util.List;<br><span class="hljs-keyword">import</span> java.util.stream.Collectors;<br> <br> <br><span class="hljs-meta">@Data</span><br><span class="hljs-meta">@NoArgsConstructor</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LoginUser</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">UserDetails</span> &#123;<br> <br>    <span class="hljs-keyword">private</span> User user;<br>        <br>    <span class="hljs-comment">//存储权限信息</span><br>    <span class="hljs-keyword">private</span> List&lt;String&gt; permissions;<br>    <br>    <br>    <span class="hljs-keyword">public</span> <span class="hljs-title function_">LoginUser</span><span class="hljs-params">(User user,List&lt;String&gt; permissions)</span> &#123;<br>        <span class="hljs-built_in">this</span>.user = user;<br>        <span class="hljs-built_in">this</span>.permissions = permissions;<br>    &#125;<br> <br> <br>    <span class="hljs-comment">//存储SpringSecurity所需要的权限信息的集合</span><br>    <span class="hljs-meta">@JSONField(serialize = false)</span><br>    <span class="hljs-keyword">private</span> List&lt;GrantedAuthority&gt; authorities;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span>  Collection&lt;? <span class="hljs-keyword">extends</span> <span class="hljs-title class_">GrantedAuthority</span>&gt; getAuthorities() &#123;<br>        <span class="hljs-keyword">if</span>(authorities!=<span class="hljs-literal">null</span>)&#123;<br>            <span class="hljs-keyword">return</span> authorities;<br>        &#125;<br>        <span class="hljs-comment">//把permissions中字符串类型的权限信息转换成GrantedAuthority对象存入authorities中</span><br>        authorities = permissions.stream().<br>                map(SimpleGrantedAuthority::<span class="hljs-keyword">new</span>)<br>                .collect(Collectors.toList());<br>        <span class="hljs-keyword">return</span> authorities;<br>    &#125;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">getPassword</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> user.getPassword();<br>    &#125;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">getUsername</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> user.getUserName();<br>    &#125;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">isAccountNonExpired</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>    &#125;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">isAccountNonLocked</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>    &#125;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">isCredentialsNonExpired</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>    &#125;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">isEnabled</span><span class="hljs-params">()</span> &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>    &#125;<br>&#125;<br> <br></code></pre></td></tr></table></figure>



<p> LoginUser 修改完后我们就可以在 UserDetailsServiceImpl 中去把权限信息封装到 LoginUser 中了。我们写死权限进行测试，后面我们再从数据库中查询权限信息。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.zty.service.impl;<br> <br><span class="hljs-keyword">import</span> com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;<br><span class="hljs-keyword">import</span> com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;<br><span class="hljs-keyword">import</span> com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;<br><span class="hljs-keyword">import</span> com.zty.domain.LoginUser;<br><span class="hljs-keyword">import</span> com.zty.domain.User;<br><span class="hljs-keyword">import</span> com.zty.mapper.UserMapper;<br><span class="hljs-keyword">import</span> org.springframework.beans.factory.annotation.Autowired;<br><span class="hljs-keyword">import</span> org.springframework.security.core.userdetails.UserDetails;<br><span class="hljs-keyword">import</span> org.springframework.security.core.userdetails.UserDetailsService;<br><span class="hljs-keyword">import</span> org.springframework.security.core.userdetails.UsernameNotFoundException;<br><span class="hljs-keyword">import</span> org.springframework.stereotype.Service;<br> <br><span class="hljs-keyword">import</span> java.util.ArrayList;<br><span class="hljs-keyword">import</span> java.util.Arrays;<br><span class="hljs-keyword">import</span> java.util.List;<br><span class="hljs-keyword">import</span> java.util.Objects;<br> <br> <br><span class="hljs-meta">@Service</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserDetailsServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">UserDetailsService</span> &#123;<br> <br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> UserMapper userMapper;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> UserDetails <span class="hljs-title function_">loadUserByUsername</span><span class="hljs-params">(String username)</span> <span class="hljs-keyword">throws</span> UsernameNotFoundException &#123;<br>        LambdaQueryWrapper&lt;User&gt; wrapper = <span class="hljs-keyword">new</span> <span class="hljs-title class_">LambdaQueryWrapper</span>&lt;&gt;();<br>        wrapper.eq(User::getUserName,username);<br>        <span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> userMapper.selectOne(wrapper);<br>        <span class="hljs-keyword">if</span>(Objects.isNull(user))&#123;<br>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(<span class="hljs-string">&quot;用户名或密码错误&quot;</span>);<br>        &#125;<br>        <span class="hljs-comment">//TODO 根据用户查询权限信息 添加到LoginUser中</span><br>        List&lt;String&gt; list = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span>&lt;&gt;(Arrays.asList(<span class="hljs-string">&quot;test&quot;</span>));<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">LoginUser</span>(user,list);<br>    &#125;<br>&#125;<br> <br></code></pre></td></tr></table></figure>



<p>3.2.3 从数据库查询权限信息</p>
<p>3.2.3.1 RBAC 权限模型</p>
<p> RBAC 权限模型（Role-Based Access Control）即：基于角色的权限控制。这是目前最常被开发者使用也是相对易用、通用权限模型。</p>
<p><img src="https://s3.bmp.ovh/imgs/2022/12/01/c949edce0625e8eb.png" srcset="/myblog/img/loading.gif" lazyload></p>
<p>3.2.3.2 准备工作</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><code class="hljs mysql">CREATE DATABASE /*!32312 IF NOT EXISTS*/`sg_security` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;<br> <br>USE `sg_security`;<br> <br>/*Table structure for table `sys_menu` */<br> <br>DROP TABLE IF EXISTS `sys_menu`;<br> <br>CREATE TABLE `sys_menu` (<br>  `id` bigint(20) NOT NULL AUTO_INCREMENT,<br>  `menu_name` varchar(64) NOT NULL DEFAULT &#x27;NULL&#x27; COMMENT &#x27;菜单名&#x27;,<br>  `path` varchar(200) DEFAULT NULL COMMENT &#x27;路由地址&#x27;,<br>  `component` varchar(255) DEFAULT NULL COMMENT &#x27;组件路径&#x27;,<br>  `visible` char(1) DEFAULT &#x27;0&#x27; COMMENT &#x27;菜单状态（0显示 1隐藏）&#x27;,<br>  `status` char(1) DEFAULT &#x27;0&#x27; COMMENT &#x27;菜单状态（0正常 1停用）&#x27;,<br>  `perms` varchar(100) DEFAULT NULL COMMENT &#x27;权限标识&#x27;,<br>  `icon` varchar(100) DEFAULT &#x27;#&#x27; COMMENT &#x27;菜单图标&#x27;,<br>  `create_by` bigint(20) DEFAULT NULL,<br>  `create_time` datetime DEFAULT NULL,<br>  `update_by` bigint(20) DEFAULT NULL,<br>  `update_time` datetime DEFAULT NULL,<br>  `del_flag` int(11) DEFAULT &#x27;0&#x27; COMMENT &#x27;是否删除（0未删除 1已删除）&#x27;,<br>  `remark` varchar(500) DEFAULT NULL COMMENT &#x27;备注&#x27;,<br>  PRIMARY KEY (`id`)<br>) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT=&#x27;菜单表&#x27;;<br> <br>/*Table structure for table `sys_role` */<br> <br>DROP TABLE IF EXISTS `sys_role`;<br> <br>CREATE TABLE `sys_role` (<br>  `id` bigint(20) NOT NULL AUTO_INCREMENT,<br>  `name` varchar(128) DEFAULT NULL,<br>  `role_key` varchar(100) DEFAULT NULL COMMENT &#x27;角色权限字符串&#x27;,<br>  `status` char(1) DEFAULT &#x27;0&#x27; COMMENT &#x27;角色状态（0正常 1停用）&#x27;,<br>  `del_flag` int(1) DEFAULT &#x27;0&#x27; COMMENT &#x27;del_flag&#x27;,<br>  `create_by` bigint(200) DEFAULT NULL,<br>  `create_time` datetime DEFAULT NULL,<br>  `update_by` bigint(200) DEFAULT NULL,<br>  `update_time` datetime DEFAULT NULL,<br>  `remark` varchar(500) DEFAULT NULL COMMENT &#x27;备注&#x27;,<br>  PRIMARY KEY (`id`)<br>) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COMMENT=&#x27;角色表&#x27;;<br> <br>/*Table structure for table `sys_role_menu` */<br> <br>DROP TABLE IF EXISTS `sys_role_menu`;<br> <br>CREATE TABLE `sys_role_menu` (<br>  `role_id` bigint(200) NOT NULL AUTO_INCREMENT COMMENT &#x27;角色ID&#x27;,<br>  `menu_id` bigint(200) NOT NULL DEFAULT &#x27;0&#x27; COMMENT &#x27;菜单id&#x27;,<br>  PRIMARY KEY (`role_id`,`menu_id`)<br>) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;<br> <br>/*Table structure for table `sys_user` */<br> <br>DROP TABLE IF EXISTS `sys_user`;<br> <br>CREATE TABLE `sys_user` (<br>  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT &#x27;主键&#x27;,<br>  `user_name` varchar(64) NOT NULL DEFAULT &#x27;NULL&#x27; COMMENT &#x27;用户名&#x27;,<br>  `nick_name` varchar(64) NOT NULL DEFAULT &#x27;NULL&#x27; COMMENT &#x27;昵称&#x27;,<br>  `password` varchar(64) NOT NULL DEFAULT &#x27;NULL&#x27; COMMENT &#x27;密码&#x27;,<br>  `status` char(1) DEFAULT &#x27;0&#x27; COMMENT &#x27;账号状态（0正常 1停用）&#x27;,<br>  `email` varchar(64) DEFAULT NULL COMMENT &#x27;邮箱&#x27;,<br>  `phonenumber` varchar(32) DEFAULT NULL COMMENT &#x27;手机号&#x27;,<br>  `sex` char(1) DEFAULT NULL COMMENT &#x27;用户性别（0男，1女，2未知）&#x27;,<br>  `avatar` varchar(128) DEFAULT NULL COMMENT &#x27;头像&#x27;,<br>  `user_type` char(1) NOT NULL DEFAULT &#x27;1&#x27; COMMENT &#x27;用户类型（0管理员，1普通用户）&#x27;,<br>  `create_by` bigint(20) DEFAULT NULL COMMENT &#x27;创建人的用户id&#x27;,<br>  `create_time` datetime DEFAULT NULL COMMENT &#x27;创建时间&#x27;,<br>  `update_by` bigint(20) DEFAULT NULL COMMENT &#x27;更新人&#x27;,<br>  `update_time` datetime DEFAULT NULL COMMENT &#x27;更新时间&#x27;,<br>  `del_flag` int(11) DEFAULT &#x27;0&#x27; COMMENT &#x27;删除标志（0代表未删除，1代表已删除）&#x27;,<br>  PRIMARY KEY (`id`)<br>) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COMMENT=&#x27;用户表&#x27;;<br> <br>/*Table structure for table `sys_user_role` */<br> <br>DROP TABLE IF EXISTS `sys_user_role`;<br> <br>CREATE TABLE `sys_user_role` (<br>  `user_id` bigint(200) NOT NULL AUTO_INCREMENT COMMENT &#x27;用户id&#x27;,<br>  `role_id` bigint(200) NOT NULL DEFAULT &#x27;0&#x27; COMMENT &#x27;角色id&#x27;,<br>  PRIMARY KEY (`user_id`,`role_id`)<br>) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;<br> <br></code></pre></td></tr></table></figure>



<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs mysql">SELECT <br>	DISTINCT m.`perms`<br>FROM<br>	sys_user_role ur<br>	LEFT JOIN `sys_role` r ON ur.`role_id` = r.`id`<br>	LEFT JOIN `sys_role_menu` rm ON ur.`role_id` = rm.`role_id`<br>	LEFT JOIN `sys_menu` m ON m.`id` = rm.`menu_id`<br>WHERE<br>	user_id = 2<br>	AND r.`status` = 0<br>	AND m.`status` = 0<br></code></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.zty.domain;<br> <br><span class="hljs-keyword">import</span> com.baomidou.mybatisplus.annotation.TableId;<br><span class="hljs-keyword">import</span> com.baomidou.mybatisplus.annotation.TableName;<br><span class="hljs-keyword">import</span> com.fasterxml.jackson.annotation.JsonInclude;<br><span class="hljs-keyword">import</span> lombok.AllArgsConstructor;<br><span class="hljs-keyword">import</span> lombok.Data;<br><span class="hljs-keyword">import</span> lombok.NoArgsConstructor;<br> <br><span class="hljs-keyword">import</span> java.io.Serializable;<br><span class="hljs-keyword">import</span> java.util.Date;<br> <br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 菜单表(Menu)实体类</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@author</span> makejava</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@since</span> 2021-11-24 15:30:08</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@TableName(value=&quot;sys_menu&quot;)</span><br><span class="hljs-meta">@Data</span><br><span class="hljs-meta">@AllArgsConstructor</span><br><span class="hljs-meta">@NoArgsConstructor</span><br><span class="hljs-meta">@JsonInclude(JsonInclude.Include.NON_NULL)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Menu</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Serializable</span> &#123;<br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">long</span> <span class="hljs-variable">serialVersionUID</span> <span class="hljs-operator">=</span> -<span class="hljs-number">54979041104113736L</span>;<br>    <br>        <span class="hljs-meta">@TableId</span><br>    <span class="hljs-keyword">private</span> Long id;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 菜单名</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> String menuName;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 路由地址</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> String path;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 组件路径</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> String component;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 菜单状态（0显示 1隐藏）</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> String visible;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 菜单状态（0正常 1停用）</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> String status;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 权限标识</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> String perms;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 菜单图标</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> String icon;<br>    <br>    <span class="hljs-keyword">private</span> Long createBy;<br>    <br>    <span class="hljs-keyword">private</span> Date createTime;<br>    <br>    <span class="hljs-keyword">private</span> Long updateBy;<br>    <br>    <span class="hljs-keyword">private</span> Date updateTime;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 是否删除（0未删除 1已删除）</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> Integer delFlag;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">    * 备注</span><br><span class="hljs-comment">    */</span><br>    <span class="hljs-keyword">private</span> String remark;<br>&#125;<br></code></pre></td></tr></table></figure>



<p>3.2.3.3 代码实现</p>
<p> 我们只需要根据用户 id 去查询到其所对应的权限信息即可。</p>
<p> 所以我们可以先定义个 mapper，其中提供一个方法可以根据 userid 查询权限信息。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> com.baomidou.mybatisplus.core.mapper.BaseMapper;<br><span class="hljs-keyword">import</span> com.zty.domain.Menu;<br> <br><span class="hljs-keyword">import</span> java.util.List;<br> <br> <br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">MenuMapper</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">BaseMapper</span>&lt;Menu&gt; &#123;<br>    List&lt;String&gt; <span class="hljs-title function_">selectPermsByUserId</span><span class="hljs-params">(Long id)</span>;<br>&#125;<br></code></pre></td></tr></table></figure>



<p> 尤其是自定义方法，所以需要创建对应的 mapper 文件，定义对应的 sql 语句</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs java">&lt;?xml version=<span class="hljs-string">&quot;1.0&quot;</span> encoding=<span class="hljs-string">&quot;UTF-8&quot;</span> ?&gt;<br>&lt;!DOCTYPE mapper PUBLIC <span class="hljs-string">&quot;-//mybatis.org//DTD Mapper 3.0//EN&quot;</span> <span class="hljs-string">&quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;</span> &gt;<br>&lt;mapper namespace=<span class="hljs-string">&quot;com.zty.mapper.MenuMapper&quot;</span>&gt;<br> <br> <br>    &lt;select id=<span class="hljs-string">&quot;selectPermsByUserId&quot;</span> resultType=<span class="hljs-string">&quot;java.lang.String&quot;</span>&gt;<br>        SELECT<br>            DISTINCT m.`perms`<br>        FROM<br>            sys_user_role ur<br>            LEFT JOIN `sys_role` r ON ur.`role_id` = r.`id`<br>            LEFT JOIN `sys_role_menu` rm ON ur.`role_id` = rm.`role_id`<br>            LEFT JOIN `sys_menu` m ON m.`id` = rm.`menu_id`<br>        <span class="hljs-type">WHERE</span><br>            <span class="hljs-variable">user_id</span> <span class="hljs-operator">=</span> #&#123;userid&#125;<br>            AND r.`status` = <span class="hljs-number">0</span><br>            AND m.`status` = <span class="hljs-number">0</span><br>    &lt;/select&gt;<br>&lt;/mapper&gt;<br></code></pre></td></tr></table></figure>



<p> 在 application.yml 中配置 mapperXML 文件的位置</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java">spring:<br>  datasource:<br>    url: jdbc:mysql:<span class="hljs-comment">//localhost:3306/security?characterEncoding=utf-8&amp;serverTimezone=UTC</span><br>    username: root<br>    password: root<br>    driver-class-name: com.mysql.cj.jdbc.Driver<br>  redis:<br>    host: localhost<br>    port: <span class="hljs-number">6379</span><br>mybatis-plus:<br>  mapper-locations: classpath*:/mapper<span class="hljs-comment">/**/*.xml </span><br><span class="hljs-comment"> </span><br></code></pre></td></tr></table></figure>



<p> 然后我们可以在 UserDetailsServiceImpl 中去调用该 mapper 的方法查询权限信息封装到 LoginUser 对象中即可。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Service</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserDetailsServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">UserDetailsService</span> &#123;<br> <br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> UserMapper userMapper;<br> <br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> MenuMapper menuMapper;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> UserDetails <span class="hljs-title function_">loadUserByUsername</span><span class="hljs-params">(String username)</span> <span class="hljs-keyword">throws</span> UsernameNotFoundException &#123;<br>        LambdaQueryWrapper&lt;User&gt; wrapper = <span class="hljs-keyword">new</span> <span class="hljs-title class_">LambdaQueryWrapper</span>&lt;&gt;();<br>        wrapper.eq(User::getUserName,username);<br>        <span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> userMapper.selectOne(wrapper);<br>        <span class="hljs-keyword">if</span>(Objects.isNull(user))&#123;<br>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(<span class="hljs-string">&quot;用户名或密码错误&quot;</span>);<br>        &#125;<br>        List&lt;String&gt; permissionKeyList =  menuMapper.selectPermsByUserId(user.getId());<br><span class="hljs-comment">//        //测试写法</span><br><span class="hljs-comment">//        List&lt;String&gt; list = new ArrayList&lt;&gt;(Arrays.asList(&quot;test&quot;));</span><br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">LoginUser</span>(user,permissionKeyList);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>



<h2 id="4-自定义失败处理"><a href="#4-自定义失败处理" class="headerlink" title="4. 自定义失败处理"></a>4. 自定义失败处理</h2><p> 我们还希望在认证失败或者是授权失败的情况下也能和我们的接口一样返回相同结构的 json，这样可以让前端能对响应进行统一的处理。要实现这个功能我们需要知道 SpringSecurity 的异常处理机制。</p>
<p> 在 SpringSecurity 中，如果我们在认证或者授权的过程中出现了异常会被 ExceptionTranslationFilter 捕获到。在 ExceptionTranslationFilter 中会去判断是认证失败还是授权失败出现的异常。</p>
<p> 如果是认证过程中出现的异常会被封装成 AuthenticationException 然后调用 <strong>AuthenticationEntryPoint</strong> 对象的方法去进行异常处理。</p>
<p> 如果是授权过程中出现的异常会被封装成 AccessDeniedException 然后调用 <strong>AccessDeniedHandler</strong> 对象的方法去进行异常处理。</p>
<p> 所以如果我们需要自定义异常处理，我们只需要自定义 AuthenticationEntryPoint 和 AccessDeniedHandler 然后配置给 SpringSecurity 即可。</p>
<p>①自定义实现类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">AccessDeniedHandlerImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">AccessDeniedHandler</span> &#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">handle</span><span class="hljs-params">(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)</span> <span class="hljs-keyword">throws</span> IOException, ServletException &#123;<br>        <span class="hljs-type">ResponseResult</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ResponseResult</span>(HttpStatus.FORBIDDEN.value(), <span class="hljs-string">&quot;权限不足&quot;</span>);<br>        <span class="hljs-type">String</span> <span class="hljs-variable">json</span> <span class="hljs-operator">=</span> JSON.toJSONString(result);<br>        WebUtils.renderString(response,json);<br> <br>    &#125;<br>&#125;<br> <br></code></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">AuthenticationEntryPointImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">AuthenticationEntryPoint</span> &#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">commence</span><span class="hljs-params">(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)</span> <span class="hljs-keyword">throws</span> IOException, ServletException &#123;<br>        <span class="hljs-type">ResponseResult</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ResponseResult</span>(HttpStatus.UNAUTHORIZED.value(), <span class="hljs-string">&quot;认证失败请重新登录&quot;</span>);<br>        <span class="hljs-type">String</span> <span class="hljs-variable">json</span> <span class="hljs-operator">=</span> JSON.toJSONString(result);<br>        WebUtils.renderString(response,json);<br>    &#125;<br>&#125;<br> <br></code></pre></td></tr></table></figure>



<p>②配置给 SpringSecurity</p>
<p> 先注入对应的处理器</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java">    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> AuthenticationEntryPoint authenticationEntryPoint;<br> <br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> AccessDeniedHandler accessDeniedHandler;<br>~~~~<br> <br>	然后我们可以使用HttpSecurity对象的方法去配置。<br> <br>~~~~java<br>        http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).<br>                accessDeniedHandler(accessDeniedHandler);<br></code></pre></td></tr></table></figure>



<h2 id="5-跨域"><a href="#5-跨域" class="headerlink" title="5. 跨域"></a>5. 跨域</h2><p> 浏览器出于安全的考虑，使用 XMLHttpRequest 对象发起 HTTP 请求时必须遵守同源策略，否则就是跨域的 HTTP 请求，默认情况下是被禁止的。 同源策略要求源相同才能正常进行通信，即协议、域名、端口号都完全一致。</p>
<p> 前后端分离项目，前端项目和后端项目一般都不是同源的，所以肯定会存在跨域请求的问题。</p>
<p> 所以我们就要处理一下，让前端能进行跨域请求。</p>
<p>①先对 SpringBoot 配置，运行跨域请求</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">CorsConfig</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">WebMvcConfigurer</span> &#123;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">addCorsMappings</span><span class="hljs-params">(CorsRegistry registry)</span> &#123;<br>      <span class="hljs-comment">// 设置允许跨域的路径</span><br>        registry.addMapping(<span class="hljs-string">&quot;/**&quot;</span>)<br>                <span class="hljs-comment">// 设置允许跨域请求的域名</span><br>                .allowedOriginPatterns(<span class="hljs-string">&quot;*&quot;</span>)<br>                <span class="hljs-comment">// 是否允许cookie</span><br>                .allowCredentials(<span class="hljs-literal">true</span>)<br>                <span class="hljs-comment">// 设置允许的请求方式</span><br>                .allowedMethods(<span class="hljs-string">&quot;GET&quot;</span>, <span class="hljs-string">&quot;POST&quot;</span>, <span class="hljs-string">&quot;DELETE&quot;</span>, <span class="hljs-string">&quot;PUT&quot;</span>)<br>                <span class="hljs-comment">// 设置允许的header属性</span><br>                .allowedHeaders(<span class="hljs-string">&quot;*&quot;</span>)<br>                <span class="hljs-comment">// 跨域允许时间</span><br>                .maxAge(<span class="hljs-number">3600</span>);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>



<p>②开启 SpringSecurity 的跨域访问</p>
<p>由于我们的资源都会收到 SpringSecurity 的保护，所以想要跨域访问还要让 SpringSecurity 运行跨域访问。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Override</span><br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">configure</span><span class="hljs-params">(HttpSecurity http)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>    http<br>            <span class="hljs-comment">//关闭csrf</span><br>            .csrf().disable()<br>            <span class="hljs-comment">//不通过Session获取SecurityContext</span><br>            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)<br>            .and()<br>            .authorizeRequests()<br>            <span class="hljs-comment">// 对于登录接口 允许匿名访问</span><br>            .antMatchers(<span class="hljs-string">&quot;/user/login&quot;</span>).anonymous()<br>            <span class="hljs-comment">// 除上面外的所有请求全部需要鉴权认证</span><br>            .anyRequest().authenticated();<br> <br>    <span class="hljs-comment">//添加过滤器</span><br>    http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);<br> <br>    <span class="hljs-comment">//配置异常处理器</span><br>    http.exceptionHandling()<br>            <span class="hljs-comment">//配置认证失败处理器</span><br>            .authenticationEntryPoint(authenticationEntryPoint)<br>            .accessDeniedHandler(accessDeniedHandler);<br> <br>    <span class="hljs-comment">//允许跨域</span><br>    http.cors();<br>&#125;<br> <br></code></pre></td></tr></table></figure>



<h2 id="6-遗留小问题"><a href="#6-遗留小问题" class="headerlink" title="6. 遗留小问题"></a>6. 遗留小问题</h2><h3 id="其它权限校验方法"><a href="#其它权限校验方法" class="headerlink" title="其它权限校验方法"></a>其它权限校验方法</h3><p> 我们前面都是使用 @PreAuthorize 注解，然后在在其中使用的是 hasAuthority 方法进行校验。SpringSecurity 还为我们提供了其它方法例如：hasAnyAuthority，hasRole，hasAnyRole 等。</p>
<p> 这里我们先不急着去介绍这些方法，我们先去理解 hasAuthority 的原理，然后再去学习其他方法你就更容易理解，而不是死记硬背区别。并且我们也可以选择定义校验方法，实现我们自己的校验逻辑。</p>
<p> hasAuthority 方法实际是执行到了 SecurityExpressionRoot 的 hasAuthority，大家只要断点调试既可知道它内部的校验原理。</p>
<p> 它内部其实是调用 authentication 的 getAuthorities 方法获取用户的权限列表。然后判断我们存入的方法参数数据在权限列表中。</p>
<p> hasAnyAuthority 方法可以传入多个权限，只有用户有其中任意一个权限都可以访问对应资源。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@PreAuthorize(&quot;hasAnyAuthority(&#x27;admin&#x27;,&#x27;test&#x27;,&#x27;system:dept:list&#x27;)&quot;)</span><br><span class="hljs-keyword">public</span> String <span class="hljs-title function_">hello</span><span class="hljs-params">()</span>&#123;<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;hello&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure>



<p> hasRole 要求有对应的角色才可以访问，但是它内部会把我们传入的参数拼接上 <strong>ROLE_</strong> 后再去比较。所以这种情况下要用用户对应的权限也要有 <strong>ROLE_</strong> 这个前缀才可以。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@PreAuthorize(&quot;hasRole(&#x27;system:dept:list&#x27;)&quot;)</span><br><span class="hljs-keyword">public</span> String <span class="hljs-title function_">hello</span><span class="hljs-params">()</span>&#123;<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;hello&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure>



<p> hasAnyRole 有任意的角色就可以访问。它内部也会把我们传入的参数拼接上 <strong>ROLE_</strong> 后再去比较。所以这种情况下要用用户对应的权限也要有 <strong>ROLE_</strong> 这个前缀才可以。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@PreAuthorize(&quot;hasAnyRole(&#x27;admin&#x27;,&#x27;system:dept:list&#x27;)&quot;)</span><br><span class="hljs-keyword">public</span> String <span class="hljs-title function_">hello</span><span class="hljs-params">()</span>&#123;<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;hello&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure>



<h3 id="自定义权限校验方法"><a href="#自定义权限校验方法" class="headerlink" title="自定义权限校验方法"></a>自定义权限校验方法</h3><p> 我们也可以定义自己的权限校验方法，在 @PreAuthorize 注解中使用我们的方法。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Component(&quot;ex&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SGExpressionRoot</span> &#123;<br> <br>    <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">hasAuthority</span><span class="hljs-params">(String authority)</span>&#123;<br>        <span class="hljs-comment">//获取当前用户的权限</span><br>        <span class="hljs-type">Authentication</span> <span class="hljs-variable">authentication</span> <span class="hljs-operator">=</span> SecurityContextHolder.getContext().getAuthentication();<br>        <span class="hljs-type">LoginUser</span> <span class="hljs-variable">loginUser</span> <span class="hljs-operator">=</span> (LoginUser) authentication.getPrincipal();<br>        List&lt;String&gt; permissions = loginUser.getPermissions();<br>        <span class="hljs-comment">//判断用户权限集合中是否存在authority</span><br>        <span class="hljs-keyword">return</span> permissions.contains(authority);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>



<p> 在 SPEL 表达式中使用 @ex 相当于获取容器中 bean 的名字未 ex 的对象。然后再调用这个对象的 hasAuthority 方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RequestMapping(&quot;/hello&quot;)</span><br><span class="hljs-meta">@PreAuthorize(&quot;@ex.hasAuthority(&#x27;system:dept:list&#x27;)&quot;)</span><br><span class="hljs-keyword">public</span> String <span class="hljs-title function_">hello</span><span class="hljs-params">()</span>&#123;<br>    <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;hello&quot;</span>;<br>&#125;<br></code></pre></td></tr></table></figure>



<h3 id="基于配置的权限控制"><a href="#基于配置的权限控制" class="headerlink" title="基于配置的权限控制"></a>基于配置的权限控制</h3><p> 我们也可以在配置类中使用使用配置的方式对资源进行权限控制。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Override</span><br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">configure</span><span class="hljs-params">(HttpSecurity http)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>    http<br>            <span class="hljs-comment">//关闭csrf</span><br>            .csrf().disable()<br>            <span class="hljs-comment">//不通过Session获取SecurityContext</span><br>            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)<br>            .and()<br>            .authorizeRequests()<br>            <span class="hljs-comment">// 对于登录接口 允许匿名访问</span><br>            .antMatchers(<span class="hljs-string">&quot;/user/login&quot;</span>).anonymous()<br>            .antMatchers(<span class="hljs-string">&quot;/testCors&quot;</span>).hasAuthority(<span class="hljs-string">&quot;system:dept:list222&quot;</span>)<br>            <span class="hljs-comment">// 除上面外的所有请求全部需要鉴权认证</span><br>            .anyRequest().authenticated();<br> <br>    <span class="hljs-comment">//添加过滤器</span><br>    http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);<br> <br>    <span class="hljs-comment">//配置异常处理器</span><br>    http.exceptionHandling()<br>            <span class="hljs-comment">//配置认证失败处理器</span><br>            .authenticationEntryPoint(authenticationEntryPoint)<br>            .accessDeniedHandler(accessDeniedHandler);<br> <br>    <span class="hljs-comment">//允许跨域</span><br>    http.cors();<br>&#125;<br></code></pre></td></tr></table></figure>



<h3 id="CSRF"><a href="#CSRF" class="headerlink" title="CSRF"></a>CSRF</h3><p> CSRF 是指跨站请求伪造（Cross-site request forgery），是 web 常见的攻击之一。</p>
<p> <a target="_blank" rel="noopener" href="https://blog.csdn.net/freeking101/article/details/86537087">https://blog.csdn.net/freeking101/article/details/86537087</a></p>
<p> SpringSecurity 去防止 CSRF 攻击的方式就是通过 csrf_token。后端会生成一个 csrf_token，前端发起请求的时候需要携带这个 csrf_token, 后端会有过滤器进行校验，如果没有携带或者是伪造的就不允许访问。</p>
<p> 我们可以发现 CSRF 攻击依靠的是 cookie 中所携带的认证信息。但是在前后端分离的项目中我们的认证信息其实是 token，而 token 并不是存储中 cookie 中，并且需要前端代码去把 token 设置到请求头中才可以，所以 CSRF 攻击也就不用担心了。</p>
<h3 id="认证成功处理器"><a href="#认证成功处理器" class="headerlink" title="认证成功处理器"></a>认证成功处理器</h3><p> 实际上在 UsernamePasswordAuthenticationFilter 进行登录认证的时候，如果登录成功了是会调用 AuthenticationSuccessHandler 的方法进行认证成功后的处理的。AuthenticationSuccessHandler 就是登录成功处理器。</p>
<p> 我们也可以自己去自定义成功处理器进行成功后的相应处理。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SGSuccessHandler</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">AuthenticationSuccessHandler</span> &#123;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">onAuthenticationSuccess</span><span class="hljs-params">(HttpServletRequest request, HttpServletResponse response, Authentication authentication)</span> <span class="hljs-keyword">throws</span> IOException, ServletException &#123;<br>        System.out.println(<span class="hljs-string">&quot;认证成功了&quot;</span>);<br>    &#125;<br>&#125;<br> <br></code></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SecurityConfig</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">WebSecurityConfigurerAdapter</span> &#123;<br> <br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> AuthenticationSuccessHandler successHandler;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">configure</span><span class="hljs-params">(HttpSecurity http)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>        http.formLogin().successHandler(successHandler);<br> <br>        http.authorizeRequests().anyRequest().authenticated();<br>    &#125;<br>&#125;<br> <br></code></pre></td></tr></table></figure>



<h3 id="认证失败处理器"><a href="#认证失败处理器" class="headerlink" title="认证失败处理器"></a>认证失败处理器</h3><p> 实际上在 UsernamePasswordAuthenticationFilter 进行登录认证的时候，如果认证失败了是会调用 AuthenticationFailureHandler 的方法进行认证失败后的处理的。AuthenticationFailureHandler 就是登录失败处理器。</p>
<p> 我们也可以自己去自定义失败处理器进行失败后的相应处理。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SGFailureHandler</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">AuthenticationFailureHandler</span> &#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">onAuthenticationFailure</span><span class="hljs-params">(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)</span> <span class="hljs-keyword">throws</span> IOException, ServletException &#123;<br>        System.out.println(<span class="hljs-string">&quot;认证失败了&quot;</span>);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SecurityConfig</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">WebSecurityConfigurerAdapter</span> &#123;<br> <br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> AuthenticationSuccessHandler successHandler;<br> <br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> AuthenticationFailureHandler failureHandler;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">configure</span><span class="hljs-params">(HttpSecurity http)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>        http.formLogin()<br><span class="hljs-comment">//                配置认证成功处理器</span><br>                .successHandler(successHandler)<br><span class="hljs-comment">//                配置认证失败处理器</span><br>                .failureHandler(failureHandler);<br> <br>        http.authorizeRequests().anyRequest().authenticated();<br>    &#125;<br>&#125;<br> <br></code></pre></td></tr></table></figure>



<h3 id="登出成功处理器"><a href="#登出成功处理器" class="headerlink" title="登出成功处理器"></a>登出成功处理器</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SGLogoutSuccessHandler</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">LogoutSuccessHandler</span> &#123;<br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">onLogoutSuccess</span><span class="hljs-params">(HttpServletRequest request, HttpServletResponse response, Authentication authentication)</span> <span class="hljs-keyword">throws</span> IOException, ServletException &#123;<br>        System.out.println(<span class="hljs-string">&quot;注销成功&quot;</span>);<br>    &#125;<br>&#125;<br> <br></code></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SecurityConfig</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">WebSecurityConfigurerAdapter</span> &#123;<br> <br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> AuthenticationSuccessHandler successHandler;<br> <br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> AuthenticationFailureHandler failureHandler;<br> <br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> LogoutSuccessHandler logoutSuccessHandler;<br> <br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">configure</span><span class="hljs-params">(HttpSecurity http)</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>        http.formLogin()<br><span class="hljs-comment">//                配置认证成功处理器</span><br>                .successHandler(successHandler)<br><span class="hljs-comment">//                配置认证失败处理器</span><br>                .failureHandler(failureHandler);<br> <br>        http.logout()<br>                <span class="hljs-comment">//配置注销成功处理器</span><br>                .logoutSuccessHandler(logoutSuccessHandler);<br> <br>        http.authorizeRequests().anyRequest().authenticated();<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

                
              </div>
            
            <hr/>
            <div>
              <div class="post-metas my-3">
  
    <div class="post-meta mr-3 d-flex align-items-center">
      <i class="iconfont icon-category"></i>
      

<span class="category-chains">
  
  
    
      <span class="category-chain">
        
  <a href="/myblog/categories/Java/" class="category-chain-item">Java</a>
  
  

      </span>
    
  
</span>

    </div>
  
  
    <div class="post-meta">
      <i class="iconfont icon-tags"></i>
      
        <a href="/myblog/tags/Java/">#Java</a>
      
        <a href="/myblog/tags/%E5%9F%BA%E7%A1%80/">#基础</a>
      
        <a href="/myblog/tags/SpringSecurity/">#SpringSecurity</a>
      
    </div>
  
</div>


              
  

  <div class="license-box my-3">
    <div class="license-title">
      <div>SpringSecurity学习笔记</div>
      <div>https://zty-f.gitee.io/myblog/2022/12/01/SpringSecurity学习/</div>
    </div>
    <div class="license-meta">
      
        <div class="license-meta-item">
          <div>作者</div>
          <div>ZTY</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>发布于</div>
          <div>2022年12月1日</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>更新于</div>
          <div>2024年12月28日</div>
        </div>
      
      
        <div class="license-meta-item">
          <div>许可协议</div>
          <div>
            
              
              
                <a target="_blank" href="https://creativecommons.org/licenses/by/4.0/">
                  <span class="hint--top hint--rounded" aria-label="BY - 署名">
                    <i class="iconfont icon-by"></i>
                  </span>
                </a>
              
            
          </div>
        </div>
      
    </div>
    <div class="license-icon iconfont"></div>
  </div>



              
                <div class="post-prevnext my-3">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/myblog/2022/12/02/MarkDown%E6%95%99%E7%A8%8B/" title="MarkDown编写基础教程">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">MarkDown编写基础教程</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/myblog/2022/11/25/%E7%A7%8B%E6%8B%9B%E7%AE%80%E8%AE%B0/" title="秋招简记">
                        <span class="hidden-mobile">秋招简记</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            
  <article id="comments" lazyload>
    
  <div id="valine"></div>
  <script type="text/javascript">
    Fluid.utils.loadComments('#valine', function() {
      Fluid.utils.createScript('https://lib.baomitu.com/valine/1.5.1/Valine.min.js', function() {
        var options = Object.assign(
          {"appId":"wLiqWYFHh2NEnXVcULmB4KWQ-gzGzoHsz","appKey":"GSfJMcM0GcCzp1HDNXJfd9Bt","path":"window.location.pathname","placeholder":"欢迎大家积极评论~  支持Markdown格式~","avatar":"wavatar","meta":["nick","mail","link"],"requiredFields":["nick","mail"],"pageSize":10,"lang":"zh-CN","highlight":true,"recordIP":true,"serverURLs":"https://wliqwyfh.lc-cn-n1-shared.com","emojiCDN":null,"emojiMaps":null,"enableQQ":true},
          {
            el: "#valine",
            path: window.location.pathname
          }
        )
        new Valine(options);
        Fluid.utils.waitElementVisible('#valine .vcontent', () => {
          var imgSelector = '#valine .vcontent img:not(.vemoji)';
          Fluid.plugins.imageCaption(imgSelector);
          Fluid.plugins.fancyBox(imgSelector);
        })
      });
    });
  </script>
  <noscript>Please enable JavaScript to view the comments</noscript>


  </article>


          </article>
        </div>
      </div>
    </div>

    <div class="side-col d-none d-lg-block col-lg-2">
      
  <aside class="sidebar" style="margin-left: -1rem">
    <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;目录</p>
  <div class="toc-body" id="toc-body"></div>
</div>



  </aside>


    </div>
  </div>
</div>





  



  



  



  



  







    

    
      <a id="scroll-top-button" aria-label="TOP" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v" for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>

    

    
      <div class="col-lg-7 mx-auto nopadding-x-md">
        <div class="container custom mx-auto">
          <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"> <script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/meting@2.0.1/dist/Meting.min.js"></script> <meting-js server="netease"  type="playlist"  id="7708362143" fixed="true"  mini="true" order="list" loop="all" preload="auto" list-folded="true" autoplay="true" theme="red"> </meting-js>
        </div>
      </div>
    
  </main>

  <footer>
    <div class="footer-inner">
  
    <div class="footer-content">
       <a href="https://github.com/zty-f" target="_blank" rel="nofollow noopener"><span>Copyright &copy;</span></a> <a href="http://www.zty-f.fun/" target="_blank" rel="nofollow noopener"><span>zty-f</span></a> 
    </div>
  
  
    <div class="statistics">
  
  

  
    
      <span id="busuanzi_container_site_pv" style="display: none">
        总访问量 
        <span id="busuanzi_value_site_pv"></span>
         次
      </span>
    
    
      <span id="busuanzi_container_site_uv" style="display: none">
        总访客数 
        <span id="busuanzi_value_site_uv"></span>
         人
      </span>
    
    
  
</div>

  
  
    <!-- 备案信息 ICP for China -->
    <div class="beian">
  <span>
    <a href="http://beian.miit.gov.cn/" target="_blank" rel="nofollow noopener">
      蜀ICP备2022002077号-1
    </a>
  </span>
  
</div>

  
  
</div>

  </footer>

  <!-- Scripts -->
  
  <script  src="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://lib.baomitu.com/jquery/3.6.0/jquery.min.js" ></script>
<script  src="https://lib.baomitu.com/twitter-bootstrap/4.6.1/js/bootstrap.min.js" ></script>
<script  src="/myblog/js/events.js" ></script>
<script  src="/myblog/js/plugins.js" ></script>


  <script  src="https://lib.baomitu.com/typed.js/2.0.12/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var subtitle = document.getElementById('subtitle');
      if (!subtitle || !typing) {
        return;
      }
      var text = subtitle.getAttribute('data-typed-text');
      
        typing(text);
      
    })(window, document);
  </script>




  
    <script  src="/myblog/js/img-lazyload.js" ></script>
  




  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/tocbot/4.18.2/tocbot.min.js', function() {
    var toc = jQuery('#toc');
    if (toc.length === 0 || !window.tocbot) { return; }
    var boardCtn = jQuery('#board-ctn');
    var boardTop = boardCtn.offset().top;

    window.tocbot.init(Object.assign({
      tocSelector     : '#toc-body',
      contentSelector : '.markdown-body',
      linkClass       : 'tocbot-link',
      activeLinkClass : 'tocbot-active-link',
      listClass       : 'tocbot-list',
      isCollapsedClass: 'tocbot-is-collapsed',
      collapsibleClass: 'tocbot-is-collapsible',
      scrollSmooth    : true,
      includeTitleTags: true,
      headingsOffset  : -boardTop,
    }, CONFIG.toc));
    if (toc.find('.toc-list-item').length > 0) {
      toc.css('visibility', 'visible');
    }

    Fluid.events.registerRefreshCallback(function() {
      if ('tocbot' in window) {
        tocbot.refresh();
        var toc = jQuery('#toc');
        if (toc.length === 0 || !tocbot) {
          return;
        }
        if (toc.find('.toc-list-item').length > 0) {
          toc.css('visibility', 'visible');
        }
      }
    });
  });
</script>


  <script src=https://lib.baomitu.com/clipboard.js/2.0.11/clipboard.min.js></script>

  <script>Fluid.plugins.codeWidget();</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/anchor-js/4.3.1/anchor.min.js', function() {
    window.anchors.options = {
      placement: CONFIG.anchorjs.placement,
      visible  : CONFIG.anchorjs.visible
    };
    if (CONFIG.anchorjs.icon) {
      window.anchors.options.icon = CONFIG.anchorjs.icon;
    }
    var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
    var res = [];
    for (var item of el) {
      res.push('.markdown-body > ' + item.trim());
    }
    if (CONFIG.anchorjs.placement === 'left') {
      window.anchors.options.class = 'anchorjs-link-left';
    }
    window.anchors.add(res.join(', '));

    Fluid.events.registerRefreshCallback(function() {
      if ('anchors' in window) {
        anchors.removeAll();
        var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
        var res = [];
        for (var item of el) {
          res.push('.markdown-body > ' + item.trim());
        }
        if (CONFIG.anchorjs.placement === 'left') {
          anchors.options.class = 'anchorjs-link-left';
        }
        anchors.add(res.join(', '));
      }
    });
  });
</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.js', function() {
    Fluid.plugins.fancyBox();
  });
</script>


  <script>Fluid.plugins.imageCaption();</script>

  <script  src="/myblog/js/local-search.js" ></script>

  <script defer src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" ></script>





<!-- 主题的启动项，将它保持在最底部 -->
<!-- the boot of the theme, keep it at the bottom -->
<script  src="/myblog/js/boot.js" ></script>


  

  <noscript>
    <div class="noscript-warning">博客在允许 JavaScript 运行的环境下浏览效果更佳</div>
  </noscript>
</body>
</html>
